Rekordy (F#)
Rekordy stanowią prosty agregatów nazwanych wartości opcjonalnie z członków.
[ attributes ]
type [accessibility-modifier] typename = {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
member-list
Uwagi
W poprzednich składni typename jest nazwą typu rekordu, label1 i label2 są nazwy wartości określone jako etykiety, i type1 i type2 typy te wartości.member-listto opcjonalna lista elementów członkowskich typu.
Poniżej przedstawiono kilka przykładów.
type Point = { x : float; y: float; z: float; }
type Customer = { First : string; Last: string; SSN: uint32; AccountNumber : uint32; }
Gdy każda etykieta jest w osobnym wierszu, średnik, jest opcjonalny.
Wartości można ustawić w wyrażeniach, znane jako wyrażeń rekordu.Kompilator ustala typ z etykiety (Jeśli etykiety są wystarczająco różne od tych innych typów rekordów).Nawiasy klamrowe ({}), należy wpisać wyrażenie rekordu.Poniższy kod ilustruje wyrażenie rekord, który inicjuje rekordu z trzech elementów float etykiet x, y i z.
let mypoint = { x = 1.0; y = 1.0; z = -1.0; }
Skrócona forma nie należy używać, jeśli może być innego typu, który także ma takie same etykiety.
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; }
Etykiety najbardziej niedawno deklarowanego typu pierwszeństwo typu poprzednio zadeklarowanych w poprzednim przykładzie tak mypoint3D jest niezamierzone za Point3D.Można jawnie określić typ rekordu, jak w poniższym kodzie.
let myPoint1 = { Point.x = 1.0; y = 1.0; z = 0.0; }
Metody mogą być definiowane dla typów rekordów tylko dla typu klasy.
Tworzenie rekordów przy użyciu wyrażeń rekordu
Rekordy można zainicjować za pomocą etykiet, które są zdefiniowane w rekordzie.Wyrażenie to jest określone jako wyrażenie rekordu.Użyj nawiasów klamrowych należy wpisać wyrażenie rekordu i używać średnika jako ogranicznika.
Poniższy przykład pokazuje, jak utworzyć rekordu.
type MyRecord = {
X: int;
Y: int;
Z: int
}
let myRecord1 = { X = 1; Y = 2; Z = 3; }
Średnikami po ostatniego pola w wyrażeniu rekordu i w definicji typu są opcjonalne, niezależnie od tego, czy pola są w jednym wierszu.
Podczas tworzenia rekordu, należy podać wartości dla każdego pola.Nie można odwołać się do wartości innych pól w wyrażeniu inicjowania dla dowolnego pola.
Poniższy kod typu myRecord2 wywnioskować na podstawie nazwy pól.Opcjonalnie można jawnie określić nazwę typu.
let myRecord2 = { MyRecord.X = 1; MyRecord.Y = 2; MyRecord.Z = 3 }
Inną formę budowy rekordu może być przydatne w trzeba skopiować istniejący rekord i ewentualnie zmieniać niektóre wartości pola.Poniższy wiersz kodu ilustruje to.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Ta forma wyrażenia rekord jest nazywana kopiowania i zaktualizować rekord wyrażenie.
Rekordy są niezmienne domyślnie; można jednak łatwo utworzyć zmodyfikowanych rekordów za pomocą wyrażenia kopii i aktualizacji.Można także jawnie określić tych pól.
type Car = {
Make : string
Model : string
mutable Odometer : int
}
let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
Nie używaj atrybutu DefaultValue z pól rekordu.Lepszym rozwiązaniem jest zdefiniowanie domyślnego wystąpienia rekordów z polami zawierającymi wartości domyślne, a następnie używać kopii i zaktualizować rekord wyrażenie ustawić żadnych pól, które różnią się od wartości domyślne.
// 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 }
Wzorzec dopasowania z rekordami
Rekordy można się z dopasowywania do wzorca.Można jawnie określić niektóre pola i są dostępne zmienne dla innych pól, które zostanie przypisany podczas pasują.Poniższy przykład kodu ilustruje to.
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 }
Dane wyjściowe ten kod wygląda następująco:
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Różnice między rekordami i klasy
Pola rekordu różnią się od klas, że są one narażone automatycznie jako właściwości i są one używane do tworzenia i kopiowanie rekordów.Budowa rekordu również różni się od klasy budowy.W polu Typ rekordu nie można zdefiniować konstruktora.Zamiast tego stosuje się składni budowy, opisane w tym temacie.Klas nie ma bezpośredniego związku między parametry konstruktora, pola i właściwości.
Typy Unii i struktury, takie jak rekordy mają semantykę strukturalnych równości.Klasy mają odniesienie semantykę równości.Poniższy przykład kodu pokazuje to.
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."
Jeśli piszesz sam kod z klas obiektów klasy dwóch będzie nierówne, ponieważ dwie wartości stanowiłaby dwóch obiektów na stercie i mogłoby być porównywane tylko adresy (chyba że zastępuje typu klasy System.Object.Equals metody).