Sdílet prostřednictvím


Záznamy (F#)

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 =
    { [ mutable ] label1 : type1;
      [ mutable ] label2 : type2;
      ... }
    [ member-list ]

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 dokládá.

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()

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.

Viz také