Teilen über


Anonyme Aufzeichnungen

Anonyme Datensätze sind einfache Aggregate benannter Werte, die nicht vor der Verwendung deklariert werden müssen. Sie können sie entweder als Struktur oder Als Verweistypen deklarieren. Sie sind standardmäßig Referenztypen.

Syntax

Die folgenden Beispiele veranschaulichen die Syntax des anonymen Datensatzes. Elemente, die mit [item] gekennzeichnet sind, sind optional.

// 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; ...|}) ...

Grundlegende Nutzung

Anonyme Datensätze werden am besten als F#-Datensatztypen betrachtet, die vor der Instanziierung nicht deklariert werden müssen.

Hier erfahren Sie beispielsweise, wie Sie mit einer Funktion interagieren können, die einen anonymen Datensatz erzeugt:

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

Im folgenden Beispiel wird das vorherige mit einer printCircleStats Funktion erweitert, die einen anonymen Datensatz als Eingabe verwendet:

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

Beim Aufrufen von printCircleStats mit einem anonymen Datensatztyp, der nicht über dieselbe „Form“ wie der Eingabetyp verfügt, tritt ein Kompilierungsfehler auf:

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"]'

Anonyme Strukturdatensätze

Anonyme Datensätze können auch als Struktur mit dem optionalen struct Schlüsselwort definiert werden. Im folgenden Beispiel wird das vorherige Beispiel erweitert, indem ein anonymer Strukturdatensatz erstellt und genutzt wird:

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

Strukturrückschluss

Anonyme Struct-Datensätze ermöglichen auch die "Strukturinferenz", bei der Sie das struct Schlüsselwort an der Aufrufstelle nicht angeben müssen. In diesem Beispiel elidieren Sie das struct Schlüsselwort beim Aufrufen 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 |}

Das umgekehrte Muster, bei dem struct angegeben wird, wenn der Eingabetyp kein anonymer Strukturdatensatz ist, kann nicht kompiliert werden.

Einbetten anonymer Datensätze in andere Typen

Es ist nützlich, diskriminierte Gewerkschaften zu deklarieren, deren Fälle Datensätze sind. Wenn die Daten in den Datensätzen jedoch derselbe Typ wie die diskriminierte Vereinigung sind, müssen Sie alle Typen als sich gegenseitig rekursiv definieren. Die Verwendung anonymer Datensätze vermeidet diese Einschränkung. Es folgt ein Beispiel für einen Typ und eine Funktion mit Musterabgleich:

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

Kopieren und Aktualisieren von Ausdrücken

Anonyme Datensätze unterstützen die Konstruktion mit Kopier- und Aktualisierungsausdrücken. Hier erfahren Sie beispielsweise, wie Sie eine neue Instanz eines anonymen Datensatzes erstellen können, der die Daten eines vorhandenen Datensatzes kopiert:

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

Im Gegensatz zu benannten Datensätzen können Sie mit anonymen Datensätzen jedoch ganz unterschiedliche Formulare mit Kopier- und Aktualisierungsausdrücken erstellen. Im folgenden Beispiel wird derselbe anonyme Datensatz aus dem vorherigen Beispiel verwendet und in einen neuen anonymen Datensatz erweitert:

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

Es ist auch möglich, anonyme Datensätze aus benannten Datensatz-Instanzen zu erstellen.

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

Sie können auch Daten in und aus Verweis- und anonymen Strukturdatensätzen kopieren:

// 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 |}

Eigenschaften von anonymen Datensätzen

Anonyme Datensätze weisen eine Reihe von Merkmalen auf, die wesentlich sind, um zu verstehen, wie sie verwendet werden können.

Anonyme Datensätze verwenden strukturelle Gleichheit und Vergleichbarkeit

Wie Datensatztypen sind anonyme Datensätze strukturell gleichzusetzen und vergleichbar. Dies gilt nur, wenn alle Komponententypen Gleichheit und Vergleich unterstützen, z. B. bei Datensatztypen. Zur Unterstützung der Gleichheit oder des Vergleichs müssen zwei anonyme Datensätze das gleiche "Shape" aufweisen.

{| 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|}

Anonyme Datensätze sind serialisierbar

Sie können anonyme Datensätze genauso serialisieren wie bei benannten Datensätzen. Hier ist ein Beispiel mit 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}"

Anonyme Datensätze sind nützlich, um einfache Daten über ein Netzwerk zu senden, ohne eine Domäne für Ihre serialisierten/deserialisierten Typen vorab zu definieren.

Anonyme Datensätze arbeiten mit anonymen C#-Typen zusammen

Es ist möglich, eine .NET-API zu verwenden, die die Verwendung anonymer C#-Typen erfordert. Anonyme C#-Typen sind durch die Verwendung anonymer Datensätze trivial zu handhaben. Das folgende Beispiel zeigt, wie anonyme Datensätze verwendet werden, um eine LINQ-Überladung aufzurufen, die einen anonymen Typ erfordert:

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}"

Es gibt eine Vielzahl anderer APIs, die in .NET verwendet werden, die die Verwendung der Übergabe eines anonymen Typs erfordern. Anonyme Datensätze sind Ihr Tool für die Arbeit mit ihnen.

Einschränkungen

Anonyme Datensätze haben einige Einschränkungen in ihrer Verwendung. Einige sind ihrem Design inhärent, aber andere können sich ändern.

Einschränkungen beim Musterabgleich

Anonyme Datensätze unterstützen nicht den Musterabgleich, im Gegensatz zu benannten Datensätzen. Es gibt drei Gründe:

  1. Ein Muster müsste jedes Feld eines anonymen Datensatzes berücksichtigen, im Gegensatz zu benannten Datensatztypen. Dies liegt daran, dass anonyme Datensätze keine strukturelle Untertypisierung unterstützen – sie erfordern einen exakten Feldabgleich.
  2. Aufgrund von (1) gibt es keine Möglichkeit, zusätzliche Muster in einem Mustervergleichsausdruck zu haben, da jedes unterschiedliche Muster einen anderen anonymen Datensatztyp impliziert.
  3. Aufgrund von (2) ist jedes anonyme Datensatzmuster ausführlicher als die Verwendung der „Punkt“-Notation.

Es gibt einen Vorschlag für eine offene Sprache, um mustervergleiche in eingeschränkten Kontexten zuzulassen.

Einschränkungen bei der Veränderbarkeit

Es ist derzeit nicht möglich, einen anonymen Datensatz mit mutable Daten zu definieren. Es gibt einen offenen Sprachvorschlag , um veränderbare Daten zuzulassen.

Einschränkungen bei anonymen Strukturdatensätzen

Es ist nicht möglich, anonyme Strukturdatensätze als IsByRefLike oder IsReadOnly zu deklarieren. Es gibt einen offenen Sprachvorschlag für anonyme IsByRefLike- und IsReadOnly-Datensätze.