Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Záznamy představují jednoduché agregace pojmenovaných hodnot, volitelně s členy. Mohou to být struktury nebo odkazové typy. Ve výchozím nastavení se jedná o odkazové typy.
Syntaxe
[ attributes ]
type [accessibility-modifier] typename =
[accessibility-modifier] {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
[ member-list ]
Předepisuje accessibility modifiertypename viditelnost celého typu a je public ve výchozím nastavení.
accessibility modifier Druhá má vliv pouze na konstruktor a pole.
Poznámky
V předchozí syntaxi je název typu záznamu, popisek1 a popisek2 jsou názvy hodnot, které se označují jako popisky, a type1 a type2 jsou typy těchto hodnot.
seznam členů je volitelný seznam členů pro typ. Atribut můžete použít [<Struct>] k vytvoření záznamu struktury místo záznamu, který je typem odkazu.
Dále je uvedeno několik příkladů.
// 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 }
Pokud je každý popisek na samostatném řádku, je středník nepovinný.
Hodnoty můžete nastavit ve výrazech, které se označují jako výrazy záznamů. Kompilátor odvodí typ z použitých popisků (pokud jsou popisky dostatečně odlišné od popisků jiných typů záznamů). Složené závorky ({ }) uzavře výraz záznamu. Následující kód ukazuje výraz záznamu, který inicializuje záznam se třemi plovoucími prvky s popisky xy a z.
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
Nepoužívejte zkrácený formulář, pokud může existovat jiný typ, který má také stejné popisky.
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 }
Popisky naposledy deklarovaného typu mají přednost před popisky dříve deklarovaného typu, takže v předchozím příkladu mypoint3D je odvozen jako Point3D. Typ záznamu můžete explicitně zadat jako v následujícím kódu.
let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0 }
Metody lze definovat pro typy záznamů stejně jako pro typy tříd.
Vytváření záznamů pomocí výrazů záznamů
Záznamy můžete inicializovat pomocí popisků definovaných v záznamu. Výraz, který to dělá, se označuje jako výraz záznamu. Pomocí složených závorek uzavřete výraz záznamu a jako oddělovač použijte středník.
Následující příklad ukazuje, jak vytvořit záznam.
type MyRecord = { X: int; Y: int; Z: int }
let myRecord1 = { X = 1; Y = 2; Z = 3 }
Středníky za posledním polem ve výrazu záznamu a v definici typu jsou volitelné bez ohledu na to, jestli jsou pole všechna na jednom řádku.
Při vytváření záznamu je nutné zadat hodnoty pro každé pole. Nelze odkazovat na hodnoty jiných polí ve výrazu inicializace pro jakékoli pole.
V následujícím kódu je typ myRecord2 odvozený z názvů polí. Volitelně můžete explicitně zadat název typu.
let myRecord2 =
{ MyRecord.X = 1
MyRecord.Y = 2
MyRecord.Z = 3 }
Další forma vytváření záznamů může být užitečná, když potřebujete zkopírovat existující záznam a případně změnit některé hodnoty polí. Následující řádek kódu to ilustruje.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Tato forma výrazu záznamu se nazývá výraz pro kopírování a aktualizaci záznamu.
Záznamy jsou ve výchozím nastavení neměnné; upravené záznamy však můžete snadno vytvořit pomocí výrazu kopírování a aktualizace. Můžete také explicitně zadat proměnlivé pole.
type Car =
{ Make: string
Model: string
mutable Odometer: int }
let myCar =
{ Make = "Fabrikam"
Model = "Coupe"
Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
Nepoužívejte atribut DefaultValue s poli záznamů. Lepším přístupem je definovat výchozí instance záznamů s poli inicializovanými na výchozí hodnoty a pak pomocí výrazu kopírování a aktualizace záznamu nastavit všechna pole, která se liší od výchozích hodnot.
// 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 }
Vytváření vzájemně rekurzivních záznamů
Někdy při vytváření záznamu může být vhodné, aby závisel na jiném typu, který byste chtěli definovat později. Jedná se o chybu kompilace, pokud nedefinujete typy záznamů, které se mají vzájemně rekurzivně rekurzivní.
Definování vzájemně rekurzivních záznamů se provádí pomocí klíčového and slova. To vám umožní propojit 2 nebo více typů záznamů dohromady.
Například následující kód definuje Person a Address typ jako vzájemně rekurzivní:
// 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 }
Pokud chcete vytvořit instance obou instancí, postupujte takto:
// 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
}
}
Pokud byste definovali předchozí příklad bez klíčového and slova, nezkompiloval by se. Klíčové and slovo se vyžaduje pro vzájemně rekurzivní definice.
Porovnávání vzorů se záznamy
Záznamy lze použít s porovnávání vzorů. Můžete zadat některá pole explicitně a zadat proměnné pro jiná pole, která budou přiřazena při výskytu shody. Následující příklad kódu to ilustruje.
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 }
Výstup tohoto kódu je následující.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Záznamy a členové
Členy můžete u záznamů zadat podobně jako u tříd. Pole nejsou podporována. Běžným přístupem je definovat Default statický člen pro snadnou konstrukci záznamů:
type Person =
{ Name: string
Age: int
Address: string }
static member Default =
{ Name = "Phillip"
Age = 12
Address = "123 happy fun street" }
let defaultPerson = Person.Default
Pokud použijete identifikátor sebe sama, odkazuje tento identifikátor na instanci záznamu, jehož člen je volána:
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()
Modifikátory přístupnosti u záznamů
Následující kód znázorňuje použití modifikátorů přístupnosti. V projektu jsou tři soubory: Module1.fs, Test1.fsa Test2.fs. Interní typ záznamu a typ záznamu s privátním konstruktorem jsou definovány v modulu 1.
// Module1.fs
module Module1
type internal internalRecd = { X: int }
type recdWithInternalCtor = private { Y: int }
Test1.fs V souboru musí být interní záznam inicializován s modifikátorem internal přístupu, je to proto, že úroveň ochrany hodnoty a záznam se musí shodovat a oba musí patřit do stejného sestavení.
// 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 V souboru nelze záznam s privátním konstruktorem inicializovat přímo kvůli úrovni ochrany konstruktoru.
// Test2.fs
module Test2
open Module1
let myRecdWithInternalCtor = { Y = 6 } // This line will cause a compiler error.
Další informace o modifikátorech přístupnosti najdete v článku Řízení přístupu .
Rozdíly mezi záznamy a třídami
Pole záznamů se liší od polí třídy v tom, že jsou automaticky vystavena jako vlastnosti a používají se při vytváření a kopírování záznamů. Konstrukce záznamů se také liší od konstrukce třídy. V typu záznamu nelze definovat konstruktor. Místo toho platí syntaxe konstrukce popsaná v tomto tématu. Třídy nemají žádný přímý vztah mezi parametry konstruktoru, poli a vlastnostmi.
Stejně jako sjednocení a typy struktur mají záznamy sémantiku strukturální rovnosti. Třídy mají sémantiku rovnosti odkazů. Následující příklad kódu ukazuje toto.
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."
Výstup tohoto kódu je následující:
The records are equal.
Pokud napíšete stejný kód s třídami, dva objekty třídy by byly nerovné, protože dvě hodnoty by představovaly dva objekty v haldě a pouze adresy by byly porovnány (pokud typ třídy přepíše metodu System.Object.Equals ).
Pokud potřebujete odkazovat na rovnost záznamů, přidejte atribut [<ReferenceEquality>] nad záznam.