레코드는 필요에 따라 멤버를 사용하여 명명된 값의 단순 집계를 나타냅니다. 구조체 또는 참조 형식일 수 있습니다. 기본적으로 참조 형식입니다.
문법
[ attributes ]
type [accessibility-modifier] typename =
[accessibility-modifier] {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
[ member-list ]
이전 typename 은 accessibility modifier 전체 형식의 표시 유형에 영향을 미치며 기본적으로 적용됩니다public. 두 번째는 accessibility modifier 생성자와 필드에만 영향을 줍니다.
비고
이전 구문에서 typename 은 레코드 형식의 이름이고 label1 과 label2 는 레이블이라고 하는 값의 이름이며 type1 과 type2 는 이러한 값의 형식입니다.
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 }
상호 재귀 레코드 만들기
레코드를 만들 때 나중에 정의하려는 다른 형식에 따라 레코드를 만들 수 있습니다. 상호 재귀적으로 레코드 형식을 정의하지 않는 한 컴파일 오류입니다.
상호 재귀 레코드 정의는 키워드를 사용하여 and 수행됩니다. 이렇게 하면 2개 이상의 레코드 형식을 함께 연결할 수 있습니다.
예를 들어 다음 코드는 a Person 및 Address 형식을 상호 재귀적으로 정의합니다.
// 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
}
}
키워드 없이 이전 예제를 and 정의하면 컴파일되지 않습니다.
and 키워드는 상호 재귀 정의에 필요합니다.
레코드와 패턴 일치
패턴 일치와 함께 레코드를 사용할 수 있습니다. 일부 필드를 명시적으로 지정하고 일치가 발생할 때 할당될 다른 필드에 대한 변수를 제공할 수 있습니다. 다음 코드 예제에서는 이를 보여 줍니다.
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()
레코드의 접근성 한정자
다음 코드에서는 접근성 한정자의 사용을 보여 줍니다. 프로젝트에Module1.fsTest1.fs는 다음과 같은 세 개의 파일이 있습니다Test2.fs. 내부 레코드 형식 및 프라이빗 생성자가 있는 레코드 형식은 Module1에서 정의됩니다.
// Module1.fs
module Module1
type internal internalRecd = { X: int }
type recdWithInternalCtor = private { Y: int }
Test1.fs 파일에서 내부 레코드는 액세스 한정자를 사용하여 internal 초기화되어야 합니다. 이는 값과 레코드의 보호 수준이 일치해야 하고 둘 다 동일한 어셈블리에 속해야 하기 때문입니다.
// Test1.fs
module Test1
open Module1
let myInternalRecd1 = { X = 2 } // This line will cause a compiler error.
let internal myInternalRecd2 = { X = 4 } // This is OK
Test2.fs 파일에서 프라이빗 생성자가 있는 레코드는 생성자의 보호 수준으로 인해 직접 초기화할 수 없습니다.
// Test2.fs
module Test2
open Module1
let myRecdWithInternalCtor = { Y = 6 } // This line will cause a compiler error.
접근성 한정자에 대한 자세한 내용은 Access Control 문서를 참조하세요.
레코드와 클래스의 차이점
레코드 필드는 속성으로 자동으로 노출되고 레코드를 만들고 복사하는 데 사용된다는 점에서 클래스 필드와 다릅니다. 레코드 생성도 클래스 생성과 다릅니다. 레코드 형식에서는 생성자를 정의할 수 없습니다. 대신 이 항목에 설명된 생성 구문이 적용됩니다. 클래스는 생성자 매개 변수, 필드 및 속성 간에 직접적인 관계가 없습니다.
공용 구조체 및 구조체 형식과 마찬가지로 레코드에는 구조적 같음 의미 체계가 있습니다. 클래스에는 참조 같음 의미 체계가 있습니다. 다음 코드 예제에서는 이를 보여 줍니다.
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>] 추가합니다.
참고하십시오
.NET