레코드(F#)
레코드는 명명된 값의 간단한 집계(경우에 따라 멤버가 포함된)를 나타냅니다. 구조체 또는 참조 형식일 수 있습니다. 기본적으로 참조 형식입니다.
구문
[ attributes ]
type [accessibility-modifier] typename =
{ [ mutable ] label1 : type1;
[ mutable ] label2 : type2;
... }
[ member-list ]
설명
이전 구문에서 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 요소를 사용하여 레코드를 초기화하는 레코드 식을 보여 줍니다 x
y
z
.
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 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
}
}
키워드(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>]
추가합니다.
참고 항목
.NET