Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
I record anonimi sono semplici aggregazioni di valori denominati che non devono essere dichiarati prima dell'uso. È possibile dichiararli come struct o tipi di riferimento. Sono tipi di riferimento per impostazione predefinita.
Sintassi
Negli esempi seguenti viene illustrata la sintassi dei record anonimi. Gli elementi delimitati come [item] sono facoltativi.
// 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; ...|}) ...
Utilizzo di base
I record anonimi sono considerati come tipi di record F# che non devono essere dichiarati prima dell'istanziazione.
Ad esempio, qui come interagire con una funzione che produce un record anonimo:
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
Nell'esempio seguente viene espansa quella precedente con una printCircleStats funzione che accetta un record anonimo come input:
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
La chiamata printCircleStats con qualsiasi tipo di record anonimo che non abbia la stessa "forma" del tipo di input non verrà compilata.
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"]'
Struct registri anonimi
I record anonimi possono anche essere definiti come struct con la parola chiave facoltativa struct . L'esempio seguente aumenta quello precedente producendo e utilizzando un record anonimo struct:
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
Inferenza di structness
I record anonimi struct consentono anche l'inferenza "structness" in cui non è necessario specificare la struct parola chiave nel sito di chiamata. In questo esempio si elide la struct parola chiave quando si chiama 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 |}
Il modello inverso, che specifica struct quando il tipo di input non è un record anonimo di struct, non riuscirà a compilare.
Incorporamento di record anonimi all'interno di altri tipi
È utile dichiarare unioni discriminate i cui casi sono record. Tuttavia, se i dati nei record sono dello stesso tipo dell'unione discriminata, è necessario definire tutti i tipi come ricorsivi a vicenda. L'uso di record anonimi evita questa restrizione. Di seguito è riportato un tipo e una funzione di esempio che effettuano il pattern matching su di esso.
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
Copiare e aggiornare le espressioni
I record anonimi supportano la costruzione usando espressioni di copia e aggiornamento. Ecco ad esempio come creare una nuova istanza di un record anonimo che copia i dati di un record esistente:
let data = {| X = 1; Y = 2 |}
let data' = {| data with Y = 3 |}
Tuttavia, a differenza dei record denominati, i record anonimi consentono di creare moduli completamente diversi con espressioni di copia e aggiornamento. L'esempio seguente accetta lo stesso record anonimo dell'esempio precedente e lo espande in un nuovo record anonimo:
let data = {| X = 1; Y = 2 |}
let expandedData = {| data with Z = 3 |} // Gives {| X=1; Y=2; Z=3 |}
È anche possibile costruire record anonimi da istanze di record denominati:
type R = { X: int }
let data = { X = 1 }
let data' = {| data with Y = 2 |} // Gives {| X=1; Y=2 |}
È anche possibile copiare dati da e verso record anonimi di riferimento e struct:
// 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 |}
Proprietà dei record anonimi
I record anonimi hanno una serie di caratteristiche essenziali per comprendere appieno come possono essere usati.
I record anonimi usano l'uguaglianza strutturale e il confronto
Analogamente ai tipi di record, i record anonimi sono strutturalmente equivalenti e confrontabili. Questo è vero solo se tutti i tipi costitutivi supportano l'uguaglianza e il confronto, proprio come avviene con i tipi di record. Per supportare l'uguaglianza o il confronto, due record anonimi devono avere la stessa "forma".
{| 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|}
I record anonimi sono serializzabili
È possibile serializzare record anonimi esattamente come è possibile con i record denominati. Di seguito è riportato un esempio che usa 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}"
I record anonimi sono utili per l'invio di dati leggeri in una rete senza la necessità di definire un dominio per i tipi serializzati/deserializzati in anticipo.
I record anonimi interagiscono con i tipi anonimi C#
È possibile usare un'API .NET che richiede l'uso di tipi anonimi C#. I tipi anonimi C# sono semplici da interagire con usando record anonimi. L'esempio seguente illustra come usare record anonimi per chiamare un overload LINQ che richiede un tipo anonimo:
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}"
Esistono numerose altre API usate in .NET che richiedono che venga passato un tipo anonimo. I record anonimi sono il tuo strumento per lavorare con essi.
Limitazioni
I record anonimi hanno alcune restrizioni nell'utilizzo. Alcuni sono intrinseci alla loro progettazione, ma altri sono in grado di cambiare.
Limitazioni con la corrispondenza di pattern
I record anonimi non supportano la corrispondenza di modelli, a differenza dei record denominati. Esistono tre motivi:
- Un modello deve tenere conto di ogni campo di un record anonimo, a differenza dei tipi di record denominati. Ciò è dovuto al fatto che i record anonimi non supportano la sottotipizzazione strutturale, ma richiedono la corrispondenza esatta dei campi.
- A causa di (1), non è possibile avere modelli aggiuntivi in un'espressione di corrispondenza dei criteri, poiché ogni criterio distinto implica un tipo di record anonimo diverso.
- A causa di (2), qualsiasi modello di record anonimo sarebbe più dettagliato rispetto all'uso della notazione "dot".
È disponibile un suggerimento di linguaggio aperto per consentire la corrispondenza dei criteri in contesti limitati.
Limitazioni della mutabilità
Non è attualmente possibile definire un record anonimo con mutable i dati. È disponibile un suggerimento per la lingua aperta per consentire dati modificabili.
Limitazioni con i record anonimi dello struct
Non è possibile dichiarare record anonimi struct come IsByRefLike o IsReadOnly. È disponibile un suggerimento di linguaggio aperto per IsByRefLike e IsReadOnly i record anonimi.