Anonieme records

Anonieme records zijn eenvoudige aggregaties van benoemde waarden die niet hoeven te worden gedeclareerd voor gebruik. U kunt ze declareren als structs of verwijzingstypen. Dit zijn standaard referentietypen.

Syntaxis

In de volgende voorbeelden ziet u de syntaxis van anonieme records. Items die zijn gescheiden als [item] optioneel.

// Construct an anonymous record
let value-name = [struct] {| Label1: Type1; Label2: Type2; ...|}

// Use an anonymous record as a type parameter
let value-name = Type-Name<[struct] {| Label1: Type1; Label2: Type2; ...|}>

// Define a parameter with an anonymous record as input
let function-name (arg-name: [struct] {| Label1: Type1; Label2: Type2; ...|}) ...

Basaal gebruik

Anonieme records kunnen het beste worden beschouwd als F#-recordtypen die niet hoeven te worden gedeclareerd voordat er een instantie wordt geïnstantieerd.

Hier ziet u bijvoorbeeld hoe u kunt communiceren met een functie die een anonieme record produceert:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    {| Diameter = d; Area = a; Circumference = c |}

let r = 2.0
let stats = getCircleStats r
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
    r stats.Diameter stats.Area stats.Circumference

In het volgende voorbeeld wordt de vorige uitgebreid met een printCircleStats functie die een anonieme record als invoer gebruikt:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    {| Diameter = d; Area = a; Circumference = c |}

let printCircleStats r (stats: {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats

Bellen printCircleStats met een anoniem recordtype dat niet dezelfde shape heeft als het invoertype, kan niet worden gecompileerd:

printCircleStats r {| Diameter = 2.0; Area = 4.0; MyCircumference = 12.566371 |}
// Two anonymous record types have mismatched sets of field names
// '["Area"; "Circumference"; "Diameter"]' and '["Area"; "Diameter"; "MyCircumference"]'

Anonieme Struct-records

Anonieme records kunnen ook worden gedefinieerd als struct met het optionele struct trefwoord. In het volgende voorbeeld wordt het vorige voorbeeld vergroot door een anonieme structrecord te produceren en te gebruiken:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    // Note that the keyword comes before the '{| |}' brace pair
    struct {| Area = a; Circumference = c; Diameter = d |}

// the 'struct' keyword also comes before the '{| |}' brace pair when declaring the parameter type
let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats

Structness-deductie

Anonieme struct-records maken ook 'structness-deductie' mogelijk, waarbij u het struct trefwoord niet hoeft op te geven op de oproepsite. In dit voorbeeld gebruikt u het trefwoord bij het struct aanroepen printCircleStats:


let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

printCircleStats r {| Area = 4.0; Circumference = 12.6; Diameter = 12.6 |}

Het omgekeerde patroon - opgeven struct wanneer het invoertype geen anonieme struct-record is - kan niet worden gecompileerd.

Anonieme records insluiten binnen andere typen

Het is handig om gediscrimineerde vakbonden te declareren waarvan de zaken records zijn. Maar als de gegevens in de records hetzelfde type zijn als de gediscrimineerde samenvoeging, moet u alle typen definiëren als wederzijds recursief. Het gebruik van anonieme records voorkomt deze beperking. Wat volgt is een voorbeeldtype en functie die het patroon ermee overeenkomt:

type FullName = { FirstName: string; LastName: string }

// Note that using a named record for Manager and Executive would require mutually recursive definitions.
type Employee =
    | Engineer of FullName
    | Manager of {| Name: FullName; Reports: Employee list |}
    | Executive of {| Name: FullName; Reports: Employee list; Assistant: Employee |}

let getFirstName e =
    match e with
    | Engineer fullName -> fullName.FirstName
    | Manager m -> m.Name.FirstName
    | Executive ex -> ex.Name.FirstName

Expressies kopiëren en bijwerken

Anonieme records ondersteunen constructie met kopieer- en update-expressies. U kunt bijvoorbeeld als volgt een nieuw exemplaar van een anonieme record maken waarmee een bestaande gegevens worden gekopieerd:

let data = {| X = 1; Y = 2 |}
let data' = {| data with Y = 3 |}

In tegenstelling tot benoemde records kunt u met anonieme records echter volledig verschillende formulieren maken met kopieer- en update-expressies. In het volgende voorbeeld wordt dezelfde anonieme record uit het vorige voorbeeld gebruikt en uitgebreid naar een nieuwe anonieme record:

let data = {| X = 1; Y = 2 |}
let expandedData = {| data with Z = 3 |} // Gives {| X=1; Y=2; Z=3 |}

Het is ook mogelijk om anonieme records te maken van exemplaren van benoemde records:

type R = { X: int }
let data = { X = 1 }
let data' = {| data with Y = 2 |} // Gives {| X=1; Y=2 |}

U kunt ook gegevens kopiëren naar en van verwijzingen naar en struct anonieme records:

// Copy data from a reference record into a struct anonymous record
type R1 = { X: int }
let r1 = { X = 1 }

let data1 = struct {| r1 with Y = 1 |}

// Copy data from a struct record into a reference anonymous record
[<Struct>]
type R2 = { X: int }
let r2 = { X = 1 }

let data2 = {| r1 with Y = 1 |}

// Copy the reference anonymous record data into a struct anonymous record
let data3 = struct {| data2 with Z = r2.X |}

Eigenschappen van anonieme records

Anonieme records hebben een aantal kenmerken die essentieel zijn om volledig te begrijpen hoe ze kunnen worden gebruikt.

Anonieme records zijn nominaal

Anonieme records zijn nominale typen. Ze worden het beste beschouwd als benoemde recordtypen (die ook nominaal zijn) waarvoor geen declaratie vooraf is vereist.

Bekijk het volgende voorbeeld met twee anonieme recorddeclaraties:

let x = {| X = 1 |}
let y = {| Y = 1 |}

De x waarden hebben y verschillende typen en zijn niet compatibel met elkaar. Ze zijn niet gelijkbaar en ze zijn niet vergelijkbaar. U kunt dit illustreren door een benoemd recordequivalent te overwegen:

type X = { X: int }
type Y = { Y: int }

let x = { X = 1 }
let y = { Y = 1 }

Er is niets inherent anders over anonieme records in vergelijking met hun benoemde record-equivalenten wanneer het gaat om gelijkwaardigheid of vergelijking van het type.

Anonieme records maken gebruik van structurele gelijkheid en vergelijking

Net als bij recordtypen zijn anonieme records structureel gelijkbaar en vergelijkbaar. Dit geldt alleen als alle samenstellende typen gelijkheid en vergelijking ondersteunen, zoals bij recordtypen. Om gelijkheid of vergelijking te ondersteunen, moeten twee anonieme records dezelfde 'shape' hebben.

{| a = 1+1 |} = {| a = 2 |} // true
{| a = 1+1 |} > {| a = 1 |} // true

// error FS0001: Two anonymous record types have mismatched sets of field names '["a"]' and '["a"; "b"]'
{| a = 1 + 1 |} = {| a = 2;  b = 1|}

Anonieme records kunnen worden geserialiseerbaar

U kunt anonieme records net zo serialiseren als met benoemde records. Hier volgt een voorbeeld met Newtonsoft.Json:

open Newtonsoft.Json

let phillip' = {| name="Phillip"; age=28 |}
let philStr = JsonConvert.SerializeObject(phillip')

let phillip = JsonConvert.DeserializeObject<{|name: string; age: int|}>(philStr)
printfn $"Name: {phillip.name} Age: %d{phillip.age}"

Anonieme records zijn handig voor het verzenden van lichtgewicht gegevens via een netwerk zonder dat u vooraf een domein hoeft te definiëren voor uw geserialiseerde/gedeserialiseerde typen.

Anonieme records werken samen met anonieme C#-typen

Het is mogelijk om een .NET API te gebruiken waarvoor het gebruik van anonieme C#-typen is vereist. Anonieme C#-typen zijn triviaal om mee te werken met behulp van anonieme records. In het volgende voorbeeld ziet u hoe u anonieme records gebruikt om een LINQ-overbelasting aan te roepen waarvoor een anoniem type is vereist:

open System.Linq

let names = [ "Ana"; "Felipe"; "Emilia"]
let nameGrouping = names.Select(fun n -> {| Name = n; FirstLetter = n[0] |})
for ng in nameGrouping do
    printfn $"{ng.Name} has first letter {ng.FirstLetter}"

Er zijn een groot aantal andere API's die worden gebruikt in .NET waarvoor het gebruik van het doorgeven van een anoniem type is vereist. Anonieme records zijn uw hulpprogramma voor het werken met hen.

Beperkingen

Anonieme records hebben enkele beperkingen in hun gebruik. Sommige zijn inherent aan hun ontwerp, maar andere kunnen worden gewijzigd.

Beperkingen met patroonkoppeling

Anonieme records bieden geen ondersteuning voor patroonkoppeling, in tegenstelling tot benoemde records. Er zijn drie redenen:

  1. Een patroon moet rekening houden met elk veld van een anonieme record, in tegenstelling tot benoemde recordtypen. Dit komt doordat anonieme records geen ondersteuning bieden voor structurele subtypen. Dit zijn nominale typen.
  2. Vanwege (1) is er geen mogelijkheid om extra patronen in een patroonovereenkomstexpressie te hebben, omdat elk afzonderlijk patroon een ander anoniem recordtype zou impliceren.
  3. Vanwege (2) zou elk anoniem recordpatroon uitgebreider zijn dan het gebruik van 'punt'-notatie.

Er is een open taalsuggesties om patroonkoppeling in beperkte contexten toe te staan.

Beperkingen met de mutabiliteit

Het is momenteel niet mogelijk om een anonieme record met mutable gegevens te definiëren. Er is een open taalsuggesties om veranderlijke gegevens toe te staan.

Beperkingen met anonieme structrecords

Het is niet mogelijk om anonieme records te declareren als IsByRefLike of IsReadOnly. Er is een open taalsuggesties voor IsByRefLike en IsReadOnly anonieme records.