Rekordok (F#)
A rekordok az elnevezett értékek egyszerű összesítését jelölik, opcionálisan a tagokkal. Ezek lehetnek szerkezetek vagy referenciatípusok. Alapértelmezés szerint referenciatípusok.
Syntax
[ attributes ]
type [accessibility-modifier] typename =
{ [ mutable ] label1 : type1;
[ mutable ] label2 : type2;
... }
[ member-list ]
Megjegyzések
Az előző szintaxisban a típusnév a rekordtípus neve, a címke1 és a címke2 pedig az értékek neve, más néven címkék, az 1 . és a 2 . típus pedig ezeknek az értékeknek a típusa. A taglista a típushoz tartozó tagok választható listája. Az [<Struct>]
attribútummal nem referencia típusú rekordot, hanem strukturált rekordot hozhat létre.
Az alábbiakban néhány példa látható.
// 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 }
Ha minden címke külön sorban van, a pontosvessző megadása nem kötelező.
Az értékeket rekordkifejezéseknek nevezett kifejezésekben állíthatja be. A fordító a használt címkékből következtet a típusra (ha a címkék kellően eltérnek a többi rekordtípustól). A kapcsos zárójelek ({ }) a rekordkifejezést is belefoglalják. Az alábbi kód egy rekordkifejezést mutat be, amely inicializál egy rekordot három lebegőpontos elemet tartalmazó címkével x
és y
z
.
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
Ne használja a rövidített űrlapot, ha más típus is rendelkezik ugyanazokkal a címkéket tartalmazó típussal.
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 }
A legutóbb deklarált típus címkéi elsőbbséget élveznek a korábban deklarált típus címkéinél, ezért az előző példában a rendszer arra következtet, mypoint3D
hogy Point3D
az . A rekordtípust explicit módon megadhatja, ahogyan az alábbi kódban is látható.
let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0 }
A metódusok ugyanúgy definiálhatók rekordtípusokhoz, mint az osztálytípusokhoz.
Rekordok létrehozása rekordkifejezések használatával
A rekordok inicializálásához használja a rekordban definiált címkéket. Ezt a kifejezést rekordkifejezésnek nevezzük. Kapcsos zárójelek használatával csatolja a rekordkifejezést, és a pontosvesszőt elválasztóként használja.
Az alábbi példa bemutatja, hogyan hozhat létre rekordot.
type MyRecord = { X: int; Y: int; Z: int }
let myRecord1 = { X = 1; Y = 2; Z = 3 }
A rekordkifejezés és a típusdefiníció utolsó mezőjének pontosvesszői nem kötelezőek, függetlenül attól, hogy a mezők egy sorban vannak-e.
Rekord létrehozásakor minden mezőhöz meg kell adnia az értékeket. Egyetlen mező inicializálási kifejezésében sem hivatkozhat más mezők értékeire.
Az alábbi kódban a mezők neveiből következtetünk a típusra myRecord2
. Igény szerint explicit módon is megadhatja a típusnevet.
let myRecord2 =
{ MyRecord.X = 1
MyRecord.Y = 2
MyRecord.Z = 3 }
A rekordépítés egy másik formája akkor lehet hasznos, ha át kell másolnia egy meglévő rekordot, és esetleg módosítania kell a mezőértékek egy részét. Ezt az alábbi kódsor szemlélteti.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
A rekordkifejezésnek ezt a formáját másolási és frissítési rekordkifejezésnek nevezzük.
A rekordok alapértelmezés szerint nem módosíthatók; a módosított rekordokat azonban egyszerűen létrehozhatja egy másolási és frissítési kifejezés használatával. Emellett explicit módon is megadhat egy mutable mezőt.
type Car =
{ Make: string
Model: string
mutable Odometer: int }
let myCar =
{ Make = "Fabrikam"
Model = "Coupe"
Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
Ne használja a DefaultValue attribútumot rekordmezőkkel. Jobb módszer az alapértelmezett értékekre inicializált mezőkkel rendelkező rekordok alapértelmezett példányainak definiálása, majd egy másolási és frissítési rekordkifejezés használata az alapértelmezett értékektől eltérő mezők beállításához.
// 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 }
Kölcsönösen rekurzív rekordok létrehozása
Egy rekord létrehozásakor előfordulhat, hogy a rekord egy másik típustól függ, amelyet később szeretne definiálni. Ez fordítási hiba, hacsak nem határozza meg, hogy a rekordtípusok kölcsönösen rekurzívak legyenek.
A kölcsönösen rekurzív rekordok meghatározása a and
kulcsszóval történik. Ez lehetővé teszi 2 vagy több rekordtípus összekapcsolásához.
A következő kód például kölcsönösen rekurzívként definiál egy Person
és Address
egy típust:
// 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 }
Mindkettő példányának létrehozásához tegye a következőket:
// 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
}
}
Ha az előző példát a and
kulcsszó nélkül szeretné definiálni, akkor az nem fordítható le. A and
kulcsszó a kölcsönösen rekurzív definíciókhoz szükséges.
Rekordokkal való mintaegyezés
A rekordok mintaegyeztetéssel használhatók. Megadhat bizonyos mezőket explicit módon, és megadhat változókat más mezőkhöz is, amelyek egyezés esetén lesznek hozzárendelve. Az alábbi példakód ezt szemlélteti.
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 }
A kód kimenete a következő.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Rekordok és tagok
Az osztályokhoz hasonlóan a rekordokon is megadhatja a tagokat. A mezők nem támogatottak. Gyakori módszer egy Default
statikus tag definiálása a könnyű rekordépítéshez:
type Person =
{ Name: string
Age: int
Address: string }
static member Default =
{ Name = "Phillip"
Age = 12
Address = "123 happy fun street" }
let defaultPerson = Person.Default
Ha önazonosítót használ, az azonosító annak a rekordnak a példányára utal, amelynek a tagját meghívják:
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()
Rekordok és osztályok közötti különbségek
A rekordmezők abban különböznek az osztálymezőktől, hogy azok automatikusan tulajdonságokként jelennek meg, és a rekordok létrehozásában és másolásában használatosak. A rekordépítés is eltér az osztályépítéstől. Rekordtípusban nem definiálhat konstruktort. Ehelyett az ebben a témakörben ismertetett szerkezetszintaxis érvényes. Az osztályoknak nincs közvetlen kapcsolatuk a konstruktorparaméterek, mezők és tulajdonságok között.
Az egyesítő és szerkezettípusokhoz hasonlóan a rekordok is rendelkeznek strukturális egyenlőségi szemantikával. Az osztályok egyenlőségi szemantikával rendelkeznek. Ezt az alábbi példakód szemlélteti.
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."
A kód kimenete a következő:
The records are equal.
Ha ugyanazt a kódot osztályokkal írja, a két osztályobjektum egyenlőtlen lenne, mert a két érték két objektumot jelölne a halomon, és csak a címeket hasonlítaná össze (kivéve, ha az osztálytípus felülírja a metódust System.Object.Equals
).
Ha hivatkozási egyenlőségre van szüksége a rekordokhoz, adja hozzá az attribútumot [<ReferenceEquality>]
a rekord fölé.