Megosztás a következőn keresztül:


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 yz.

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 Point3Daz . 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é.

Lásd még