레코드(F#)

레코드는 명명된 값의 간단한 집계(경우에 따라 멤버가 포함된)를 나타냅니다. 구조체 또는 참조 형식일 수 있습니다. 기본적으로 참조 형식입니다.

구문

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

설명

이전 구문에서 typename은 레코드 형식의 이름이고 label1label2는 레이블이라고 하는 값의 이름이며 type1type2는 이러한 값의 형식입니다. member-list 는 형식에 대한 멤버의 선택적 목록입니다. 이 특성을 사용하여 [<Struct>] 참조 형식인 레코드가 아닌 구조체 레코드를 만들 수 있습니다.

다음은 몇 가지 예입니다.

// 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 }

각 레이블이 별도의 줄에 있는 경우 세미콜론은 선택 사항입니다.

레코드 식이라고 하는 식에서 값을 설정할 수 있습니다. 컴파일러는 사용되는 레이블에서 형식을 유추합니다(레이블이 다른 레코드 형식과 충분히 구별되는 경우). 중괄호({ })는 레코드 식을 묶습니다. 다음 코드는 레이블이 있는 3개의 float 요소를 사용하여 레코드를 초기화하는 레코드 식을 보여 줍니다 xyz.

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

레이블이 같은 다른 형식이 있을 수 있는 경우 단축된 양식을 사용하지 마세요.

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. 다음 코드와 같이 레코드 형식을 명시적으로 지정할 수 있습니다.

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

메서드는 클래스 형식과 마찬가지로 레코드 형식에 대해 정의할 수 있습니다.

레코드 식을 사용하여 레코드 만들기

레코드에 정의된 레이블을 사용하여 레코드를 초기화할 수 있습니다. 이를 수행하는 식을 레코드 식이라고 합니다. 중괄호를 사용하여 레코드 식을 묶고 세미콜론을 구분 기호로 사용합니다.

다음 예제에서는 레코드를 만드는 방법을 보여줍니다.

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

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

레코드 식 및 형식 정의의 마지막 필드 뒤의 세미콜론은 필드가 모두 한 줄에 있는지 여부에 관계없이 선택 사항입니다.

레코드를 만들 때 각 필드에 값을 제공해야 합니다. 필드의 초기화 식에서 다른 필드의 값을 참조할 수 없습니다.

다음 코드에서 형식 myRecord2 은 필드 이름에서 유추됩니다. 필요에 따라 형식 이름을 명시적으로 지정할 수 있습니다.

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

다른 형식의 레코드 생성은 기존 레코드를 복사하고 일부 필드 값을 변경해야 하는 경우에 유용할 수 있습니다. 다음 코드 줄에서는 이를 보여 줍니다.

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

이 형식의 레코드 식은 복사 및 업데이트 레코드 식이라고 합니다.

레코드는 기본적으로 변경할 수 없습니다. 그러나 복사 및 업데이트 식을 사용하여 수정된 레코드를 쉽게 만들 수 있습니다. 변경 가능한 필드를 명시적으로 지정할 수도 있습니다.

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

let myCar =
    { Make = "Fabrikam"
      Model = "Coupe"
      Odometer = 108112 }

myCar.Odometer <- myCar.Odometer + 21

레코드 필드에 DefaultValue 특성을 사용하지 마세요. 더 나은 방법은 기본값으로 초기화된 필드를 사용하여 레코드의 기본 인스턴스를 정의한 다음, 복사 및 업데이트 레코드 식을 사용하여 기본값과 다른 필드를 설정하는 것입니다.

// 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 }

상호 재귀 레코드 만들기

레코드를 만들 때 나중에 정의하려는 다른 형식에 따라 레코드를 만들 수 있습니다. 상호 재귀적으로 레코드 형식을 정의하지 않는 한 컴파일 오류입니다.

상호 재귀 레코드 정의는 키워드(keyword) 수행 and 됩니다. 이렇게 하면 2개 이상의 레코드 형식을 함께 연결할 수 있습니다.

예를 들어 다음 코드는 a PersonAddress 형식을 상호 재귀적으로 정의합니다.

// 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 }

둘 다의 인스턴스를 만들려면 다음을 수행합니다.

// Create a Person type and use the Address type that is not defined
let rec person =
  {
      Name = "Person name"
      Age = 12
      Address =
          {
              Line1 = "line 1"
              Line2 = "line 2"
              PostCode = "abc123"
              Occupant = person
          }
  }

키워드(keyword) 없이 이전 예제를 and 정의하면 컴파일되지 않습니다. and 키워드(keyword) 상호 재귀 정의에 필요합니다.

레코드와 패턴 일치

패턴 일치와 함께 레코드를 사용할 수 있습니다. 일부 필드를 명시적으로 지정하고 일치가 발생할 때 할당될 다른 필드에 대한 변수를 제공할 수 있습니다. 다음 코드 예제에서는 그 구체적인 방법을 보여 줍니다.

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 }

이 코드의 출력은 다음과 같습니다.

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

레코드 및 멤버

클래스와 마찬가지로 레코드에 멤버를 지정할 수 있습니다. 필드에 대한 지원은 없습니다. 일반적인 방법은 쉬운 레코드 생성을 위해 정적 멤버를 Default 정의하는 것입니다.

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

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

let defaultPerson = Person.Default

자체 식별자를 사용하는 경우 해당 식별자는 멤버가 호출되는 레코드의 인스턴스를 참조합니다.

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()

레코드와 클래스의 차이점

레코드 필드는 속성으로 자동으로 노출되고 레코드를 만들고 복사하는 데 사용된다는 점에서 클래스 필드와 다릅니다. 레코드 생성도 클래스 생성과 다릅니다. 레코드 형식에서는 생성자를 정의할 수 없습니다. 대신 이 항목에 설명된 생성 구문이 적용됩니다. 클래스는 생성자 매개 변수, 필드 및 속성 간에 직접적인 관계가 없습니다.

공용 구조체 및 구조체 형식과 마찬가지로 레코드에는 구조적 같음 의미 체계가 있습니다. 클래스에는 참조 같음 의미 체계가 있습니다. 다음 코드 예제에서는 이 작업을 보여줍니다.

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 records are equal.

클래스를 사용하여 동일한 코드를 작성하는 경우 두 값이 힙의 두 개체를 나타내고 클래스 형식이 메서드를 재정 System.Object.Equals 의하지 않는 한 주소만 비교되기 때문에 두 클래스 개체가 같지 않습니다.

레코드에 대한 참조 같음이 필요한 경우 레코드 위에 특성을 [<ReferenceEquality>] 추가합니다.

참고 항목