レコードRecords

レコードは、名前付きの値の単純な集合を表しており、オプションでメンバーを含みます。Records represent simple aggregates of named values, optionally with members. 構造体と参照型のどちらでもかまいません。They can either be structs or reference types. 既定では、これらは参照型です。They are reference types by default.

構文Syntax

[ attributes ]
type [accessibility-modifier] typename =
    { [ mutable ] label1 : type1;
      [ mutable ] label2 : type2;
      ... }
    [ member-list ]

解説Remarks

前の構文では、 typenameはレコード型の名前、 label1label2]ラベル呼ばれる値の名前、 type1 と type1はこれらの値の型です。In the previous syntax, typename is the name of the record type, label1 and label2 are names of values, referred to as labels, and type1 and type2 are the types of these values. メンバーリスト は、型のメンバーの省略可能なリストです。member-list is the optional list of members for the type. 属性を使用して、参照型であるレコードでは [<Struct>] なく、構造体レコードを作成できます。You can use the [<Struct>] attribute to create a struct record rather than a record which is a reference type.

次は一部の例です。Following are some examples.

// Labels are separated by semicolons when defined on the same line.
type Point = { X: float; Y: float; Z: float; }

// You can define labels on their own line with or without a semicolon.
type Customer = 
    { First: string
      Last: string;
      SSN: uint32
      AccountNumber: uint32; }

// A struct record.
[<Struct>]
type StructPoint = 
    { X: float
      Y: float
      Z: float }

各ラベルが個別の行にある場合、セミコロンは省略可能です。When each label is on a separate line, the semicolon is optional.

レコード式と呼ばれる式に値を設定できます。You can set values in expressions known as record expressions. コンパイラは、使用されているラベルから型を推測します (ラベルが他のレコードの種類とは十分に異なる場合)。The compiler infers the type from the labels used (if the labels are sufficiently distinct from those of other record types). 中かっこ ({}) は、レコード式を囲みます。Braces ({ }) enclose the record expression. 次のコードは、とというラベルを持つ3つの float 要素を持つレコードを初期化するレコード式を示して x y z います。The following code shows a record expression that initializes a record with three float elements with labels x, y and z.

let mypoint = { X = 1.0; Y = 1.0; Z = -1.0; }

同じラベルを持つ別の型が存在する可能性がある場合は、短縮形を使用しないでください。Do not use the shortened form if there could be another type that also has the same labels.

type Point = { X: float; Y: float; Z: float; }
type Point3D = { X: float; Y: float; Z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { X = 1.0; Y = 1.0; Z = 0.0; }

最後に宣言された型のラベルは、以前に宣言された型よりも優先されます。したがって、前の例で mypoint3D は、はと推論され Point3D ます。The labels of the most recently declared type take precedence over those of the previously declared type, so in the preceding example, mypoint3D is inferred to be Point3D. レコードの種類は、次のコードのように明示的に指定できます。You can explicitly specify the record type, as in the following code.

let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0; }

メソッドは、クラス型の場合と同様に、レコード型に対して定義できます。Methods can be defined for record types just as for class types.

レコード式を使用したレコードの作成Creating Records by Using Record Expressions

レコードで定義されているラベルを使用して、レコードを初期化できます。You can initialize records by using the labels that are defined in the record. これを行う式は、 レコード式と呼ばれます。An expression that does this is referred to as a record expression. 中かっこを使用してレコード式を囲み、セミコロンを区切り記号として使用します。Use braces to enclose the record expression and use the semicolon as a delimiter.

次の例では、レコードを作成する方法を示します。The following example shows how to create a record.

type MyRecord = 
    { X: int
      Y: int
      Z: int }

let myRecord1 = { X = 1; Y = 2; Z = 3; }

レコード式の最後のフィールドの後のセミコロン、および型定義では、フィールドがすべて1行にあるかどうかにかかわらず、省略可能です。The semicolons after the last field in the record expression and in the type definition are optional, regardless of whether the fields are all in one line.

レコードを作成するときは、各フィールドに値を指定する必要があります。When you create a record, you must supply values for each field. 任意のフィールドについて、初期化式の他のフィールドの値を参照することはできません。You cannot refer to the values of other fields in the initialization expression for any field.

次のコードでは、の型は myRecord2 フィールドの名前から推論されます。In the following code, the type of myRecord2 is inferred from the names of the fields. 必要に応じて、型名を明示的に指定できます。Optionally, you can specify the type name explicitly.

let myRecord2 = { MyRecord.X = 1; MyRecord.Y = 2; MyRecord.Z = 3 }

別の形式のレコードの構築は、既存のレコードをコピーする必要があり、フィールド値の一部を変更する必要がある場合に便利です。Another form of record construction can be useful when you have to copy an existing record, and possibly change some of the field values. 次のコード行はこれを示しています。The following line of code illustrates this.

let myRecord3 = { myRecord2 with Y = 100; Z = 2 }

この形式のレコード式は、レコードの コピーと更新の式と呼ばれます。This form of the record expression is called the copy and update record expression.

既定では、レコードは変更できません。ただし、コピーと更新の式を使用して、変更されたレコードを簡単に作成できます。Records are immutable by default; however, you can easily create modified records by using a copy and update expression. また、変更可能なフィールドを明示的に指定することもできます。You can also explicitly specify a mutable field.

type Car = 
    { Make : string
      Model : string
      mutable Odometer : int }

let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21

レコードフィールドで DefaultValue 属性を使用しないでください。Don't use the DefaultValue attribute with record fields. より適切な方法は、既定値に初期化されるフィールドを持つレコードの既定のインスタンスを定義し、コピーと更新のレコード式を使用して、既定値とは異なるフィールドを設定することです。A better approach is to define default instances of records with fields that are initialized to default values and then use a copy and update record expression to set any fields that differ from the default values.

// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
    { Field1 : int
      Field2 : int }

let defaultRecord1 = { Field1 = 0; Field2 = 0 }
let defaultRecord2 = { Field1 = 1; Field2 = 25 }

// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with Field2 = 42 }

相互再帰的なレコードの作成Creating Mutually Recursive Records

レコードを作成するときに、後で定義する別の型に依存するように設定することもできます。Sometime when creating a record, you may want to have it depend on another type that you would like to define afterwards. レコードの種類が相互に再帰的に定義されている場合を除き、コンパイルエラーになります。This is a compile error unless you define the record types to be mutually recursive.

同時に再帰的なレコードを定義するには、キーワードを使用 and します。Defining mutually recursive records is done with the and keyword. これにより、2つ以上のレコードの種類をリンクすることができます。This lets you link 2 or more record types together.

たとえば、次のコードでは、 Person と型が相互に再帰的に定義され Address ています。For example, the following code defines a Person and Address type as mutually recursive:

// Create a Person type and use the Address type that is not defined
type Person =
  { Name: string
    Age: int
    Address: Address }
// Define the Address type which is used in the Person record
and Address =
  { Line1: string
    Line2: string
    PostCode: string
    Occupant: Person }

キーワードを使用せずに前の例を定義すると and 、コンパイルされません。If you were to define the previous example without the and keyword, then it would not compile. and相互再帰的な定義にはキーワードが必要です。The and keyword is required for mutually recursive definitions.

レコードを使用したパターンマッチPattern Matching with Records

レコードは、パターンマッチングで使用できます。Records can be used with pattern matching. 一部のフィールドを明示的に指定し、一致が発生したときに割り当てられる他のフィールドの変数を指定できます。You can specify some fields explicitly and provide variables for other fields that will be assigned when a match occurs. これを次のコード例に示します。The following code example illustrates this.

type Point3D = { X: float; Y: float; Z: float }
let evaluatePoint (point: Point3D) =
    match point with
    | { X = 0.0; Y = 0.0; Z = 0.0 } -> printfn "Point is at the origin."
    | { X = xVal; Y = 0.0; Z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
    | { X = 0.0; Y = yVal; Z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
    | { X = 0.0; Y = 0.0; Z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
    | { X = xVal; Y = yVal; Z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal

evaluatePoint { X = 0.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 100.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 10.0; Y = 0.0; Z = -1.0 }

このコードの出力は次のようになります。The output of this code is as follows.

Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).

レコードとメンバーRecords and members

クラスの場合と同様に、レコードのメンバーを指定できます。You can specify members on records much like you can with classes. フィールドはサポートされていません。There is no support for fields. 一般的な方法は、レコードを Default 簡単に構築できるように静的メンバーを定義することです。A common approach is to define a Default static member for easy record construction:

type Person =
  { Name: string
    Age: int
    Address: string }

    static member Default =
        { Name = "Phillip"
          Age = 12
          Address = "123 happy fun street" }

let defaultPerson = Person.Default

自己識別子を使用する場合、その識別子は、メンバーが呼び出されるレコードのインスタンスを参照します。If you use a self identifier, that identifier refers to the instance of the record whose member is called:

type Person =
  { Name: string
    Age: int
    Address: string }

    member this.WeirdToString() =
        this.Name + this.Address + string this.Age

let p = { Name = "a"; Age = 12; Address = "abc123 }
let weirdString = p.WeirdToString()

レコードとクラスの違いDifferences Between Records and Classes

レコードフィールドは、プロパティとして自動的に公開され、レコードの作成とコピーに使用されるという点でクラスとは異なります。Record fields differ from classes in that they are automatically exposed as properties, and they are used in the creation and copying of records. レコードの構築も、クラスの構築とは異なります。Record construction also differs from class construction. レコード型では、コンストラクターを定義することはできません。In a record type, you cannot define a constructor. 代わりに、このトピックで説明する構築構文が適用されます。Instead, the construction syntax described in this topic applies. クラスには、コンストラクターのパラメーター、フィールド、およびプロパティの間に直接的な関係はありません。Classes have no direct relationship between constructor parameters, fields, and properties.

Union 型や structure 型と同様に、レコードには構造的等価性のセマンティクスがあります。Like union and structure types, records have structural equality semantics. クラスには参照等値セマンティクスがあります。Classes have reference equality semantics. 次のコード例はこの処理方法を示しています。The following code example demonstrates this.

type RecordTest = { X: int; Y: int }

let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }

if (record1 = record2) then
    printfn "The records are equal."
else
    printfn "The records are unequal."

このコードの出力は次のとおりです。The output of this code is as follows:

The records are equal.

クラスを使用して同じコードを記述した場合、2つのクラスオブジェクトは等しくなります。これは、2つの値がヒープ上の2つのオブジェクトを表し、アドレスのみが比較されるためです (クラス型がメソッドをオーバーライドする場合を除き System.Object.Equals ます)。If you write the same code with classes, the two class objects would be unequal because the two values would represent two objects on the heap and only the addresses would be compared (unless the class type overrides the System.Object.Equals method).

レコードの参照の等価性が必要な場合は、レコードの上に属性を追加し [<ReferenceEquality>] ます。If you need reference equality for records, add the attribute [<ReferenceEquality>] above the record.

関連項目See also