Strutture

Una struttura è un tipo di oggetto compatto che può essere più efficiente di una classe per i tipi con una piccola quantità di dati e un comportamento semplice.

Sintassi

[ attributes ]
type [accessibility-modifier] type-name =
    struct
        type-definition-elements-and-members
    end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
    type-definition-elements-and-members

Osservazioni:

Le strutture sono tipi valore, il che significa che vengono archiviati direttamente nello stack o, quando vengono usati come campi o elementi di matrice, inline nel tipo padre. A differenza di record e classi, le strutture hanno una semantica pass-by-value. Ciò significa che sono utili principalmente per piccole aggregazioni di dati accessibili e copiati di frequente.

Nella sintassi precedente sono illustrate due forme. Il primo non è la sintassi leggera, ma è tuttavia spesso usato perché, quando si usano le struct parole chiave e end , è possibile omettere l'attributo StructAttribute , che viene visualizzato nel secondo modulo. È possibile abbreviare StructAttribute in Struct.

I membri type-definition-elements-and-members nella sintassi precedente rappresentano le dichiarazioni e le definizioni dei membri. Le strutture possono disporre di costruttori e campi modificabili e non modificabili e possono dichiarare i membri e le implementazioni dell'interfaccia. Per altre informazioni, vedere Membri.

Le strutture non possono partecipare all'ereditarietà, non possono contenere associazioni let o do e non possono contenere in modo ricorsivo campi del proprio tipo (nonostante possano contenere celle di riferimento che facciano riferimento al proprio tipo).

Poiché le strutture non consentono associazioni let, è necessario dichiarare i campi nelle strutture usando la parola chiave val. La parola chiave val definisce un campo e il relativo tipo, ma non consente l'inizializzazione. Al contrario, le dichiarazioni val sono inizializzate su zero o null. Per questo motivo, le strutture che hanno un costruttore implicito (ovvero, i parametri forniti immediatamente dopo il nome della struttura nella dichiarazione) richiedono di annotare le dichiarazioni val con l'attributo DefaultValue. Le strutture che hanno un costruttore definito supportano comunque l'inizializzazione su zero. Quindi, l'attributo DefaultValue è una dichiarazione che tale valore zero è valido per il campo. I costruttori impliciti per le strutture non eseguono alcuna azione perché le associazioni let e do non sono consentite per il tipo, ma i valori di parametro di costruttore implicito passati sono disponibili come campi privati.

I costruttori espliciti potrebbero comportare l'inizializzazione dei valori di campo. Quando si ha una struttura con un costruttore esplicito, è comunque supportata l'inizializzazione su zero. Tuttavia, non usare l'attributo DefaultValue sulle dichiarazioni val perché è in conflitto con il costruttore esplicito. Per altre informazioni sulle val dichiarazioni, vedere Campi espliciti: parola val chiave.

Gli attributi e modificatori di accessibilità sono consentiti nelle strutture e seguono le stesse regole per gli altri tipi. Per altre informazioni, vedere Attributi e Controllo di accesso.

Gli esempi di codice seguenti illustrano le definizioni delle strutture.

// In Point3D, three immutable values are defined.
// x, y, and z will be initialized to 0.0.
type Point3D =
    struct
        val x: float
        val y: float
        val z: float
    end

// In Point2D, two immutable values are defined.
// It also has a member which computes a distance between itself and another Point2D.
// Point2D has an explicit constructor.
// You can create zero-initialized instances of Point2D, or you can
// pass in arguments to initialize the values.
type Point2D =
    struct
        val X: float
        val Y: float
        new(x: float, y: float) = { X = x; Y = y }

        member this.GetDistanceFrom(p: Point2D) =
            let dX = (p.X - this.X) ** 2.0
            let dY = (p.Y - this.Y) ** 2.0

            dX + dY |> sqrt
    end

Struct ByRefLike

È possibile definire struct personalizzati che possono essere conformi alla semantica simile a byref: vedere Byrefs per altre informazioni. Questa operazione viene eseguita con l'attributo IsByRefLikeAttribute :

open System
open System.Runtime.CompilerServices

[<IsByRefLike; Struct>]
type S(count1: Span<int>, count2: Span<int>) =
    member x.Count1 = count1
    member x.Count2 = count2

IsByRefLike non implica Struct. Entrambi devono essere presenti nel tipo .

Uno struct "byrefsimile" in F# è un tipo di valore associato a stack. Non viene mai allocata nell'heap gestito. Uno byrefstruct -like è utile per la programmazione ad alte prestazioni, perché viene applicato con un set di controlli sicuri sulla durata e non acquisizione. Le regole sono:

  • Possono essere usati come parametri di funzione, parametri del metodo, variabili locali, metodo restituito.
  • Non possono essere membri statici o di istanza di una classe o di uno struct normale.
  • Non possono essere acquisiti da alcun costrutto di chiusura (async metodi o espressioni lambda).
  • Non possono essere usati come parametro generico.

Anche se queste regole limitano molto fortemente l'utilizzo, lo fanno per soddisfare la promessa di elaborazione ad alte prestazioni in modo sicuro.

Struct ReadOnly

È possibile annotare gli struct con l'attributo IsReadOnlyAttribute . Ad esempio:

[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
    member x.Count1 = count1
    member x.Count2 = count2

IsReadOnly non implica Struct. È necessario aggiungere entrambi per avere uno IsReadOnly struct.

L'uso di questo attributo genera metadati che consentono rispettivamente a F# e C# di considerarlo come inref<'T> e in ref.

La definizione di un valore modificabile all'interno di uno struct readonly genera un errore.

Record struct e unioni discriminate

È possibile rappresentare record e unioni discriminate come struct con l'attributo [<Struct>] . Per altre informazioni, vedere ogni articolo.

Vedi anche