Condividi tramite


16 Strutture

16.1 Generale

Gli struct sono simili alle classi in quanto rappresentano strutture di dati che possono contenere membri dati e membri di funzione. Tuttavia, a differenza delle classi, gli struct sono tipi valore e non richiedono l'allocazione dell'heap. Una variabile di un struct tipo contiene direttamente i dati di struct, mentre una variabile di un tipo di classe contiene un riferimento ai dati, quest'ultimo noto come oggetto .

Nota: gli struct sono particolarmente utili per piccole strutture di dati con semantica di valore. I numeri complessi, i punti di un sistema di coordinate o le coppie chiave-valore di un dizionario sono buoni esempi di struct. La chiave per queste strutture di dati è che dispongono di pochi membri dati, che non richiedono l'uso dell'ereditarietà o della semantica di riferimento, ma possono essere implementati facilmente usando la semantica dei valori in cui l'assegnazione copia il valore anziché il riferimento. nota finale

Come descritto in §8.3.5, i tipi semplici forniti da C#, ad esempio int, doublee bool, sono, infatti, tutti i tipi di struct.

16.2 Dichiarazioni di struct

16.2.1 Generale

Un struct_declaration è un type_declaration (§14.7) che dichiara un nuovo struct:

struct_declaration
    : attributes? struct_modifier* 'ref'? 'partial'? 'struct'
      identifier type_parameter_list? struct_interfaces?
      type_parameter_constraints_clause* struct_body ';'?
    ;

Un struct_declaration è costituito da un set facoltativo di attributi (§22), seguito da un set facoltativo di struct_modifier (§16.2.2), seguito da un modificatore facoltativo ref (§16.2.3), seguito da un modificatore parziale facoltativo (§15.2.7), seguito dalla parola chiave struct e da un identificatore che denomina lo struct, seguito da una specifica facoltativa di type_parameter_list (§15.2.3), seguita da una specifica facoltativa di struct_interfaces (§16.2.5), seguita da una specifica facoltativa di type_parameter_constraints-clauses (§15.2.5), seguita da un struct_body (§16.2.6), facoltativamente seguita da un punto e virgola.

Una dichiarazione di struct non deve fornire type_parameter_constraints_clausese non prevede anche un type_parameter_list.

Una dichiarazione di struct che fornisce un type_parameter_list è una dichiarazione di struct generica. Inoltre, qualsiasi struct annidato all'interno di una dichiarazione di classe generica o una dichiarazione di struct generica è una dichiarazione di struct generica, poiché gli argomenti di tipo per il tipo contenitore devono essere forniti per creare un tipo costruito (§8.4).

Una dichiarazione di struct che include una ref keyword non deve avere una parte struct_interfaces.

16.2.2 Modificatori di struct

Un struct_declaration può facoltativamente includere una sequenza di struct_modifiers:

struct_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'readonly'
    | unsafe_modifier   // unsafe code support
    ;

unsafe_modifier (§23.2) è disponibile solo nel codice non sicuro (§23).

È un errore di compilazione che lo stesso modificatore appaia più volte in una dichiarazione di struct.

readonlyAd eccezione di , i modificatori di una dichiarazione di struct hanno lo stesso significato di quelli di una dichiarazione di classe (§15.2.2).

Il readonly modificatore indica che il struct_declaration dichiara un tipo le cui istanze non sono modificabili.

Uno struct readonly presenta i vincoli seguenti:

  • Ogni campo dell'istanza deve essere dichiarato readonly anche.
  • Nessuna delle relative proprietà di istanza deve avere un set_accessor_declaration (§15.7.3).
  • Non deve dichiarare eventi di tipo campo (§15.8.2).

Quando un'istanza di uno struct readonly viene passata a un metodo, viene this considerata come un argomento/parametro di input, che non consente l'accesso in scrittura a tutti i campi dell'istanza (ad eccezione dei costruttori).

16.2.3 Modificatore di riferimento

Il ref modificatore indica che il struct_declaration dichiara un tipo le cui istanze vengono allocate nello stack di esecuzione. Questi tipi sono chiamati ref struct types. Il ref modificatore dichiara che le istanze possono contenere campi di tipo ref-like e non devono essere copiate al di fuori del proprio contesto sicuro (§16.4.12). Le regole per determinare il contesto sicuro di uno struct di riferimento sono descritte in §16.4.12.

Si tratta di un errore in fase di compilazione se viene usato un tipo di struct ref in uno dei contesti seguenti:

  • Come tipo di elemento di una matrice.
  • Come tipo dichiarato di un campo di una classe o di uno struct che non dispone del ref modificatore.
  • Essere rinchiuso in System.ValueType o System.Object.
  • Come argomento di tipo.
  • Come tipo di elemento di una tupla.
  • Metodo asincrono.
  • Iteratore.
  • Non esiste alcuna conversione da un ref struct tipo al tipo object o al tipo System.ValueType.
  • Un ref struct tipo non deve essere dichiarato per implementare alcuna interfaccia.
  • Un metodo di istanza dichiarato in object o in System.ValueType ma non sottoposto a override in un tipo ref struct non deve essere chiamato con un ricevitore di quel tipo ref struct.
  • Un metodo di istanza di tipo ref struct non deve essere catturato dalla conversione del gruppo di metodi in un tipo delegato.
  • Uno struct di riferimento non deve essere acquisito da un'espressione lambda o da una funzione locale.

Nota: un ref struct oggetto non dichiara async metodi di istanza né usa un'istruzione yield return o yield break all'interno di un metodo di istanza, perché il parametro implicito this non può essere usato in tali contesti. nota finale

Questi vincoli assicurano che una variabile di ref struct tipo non faccia riferimento alla memoria dello stack non più valida o alle variabili non più valide.

16.2.4 Modificatore parziale

Il partial modificatore indica che questo struct_declaration è una dichiarazione di tipo parziale. Più dichiarazioni parziali di struct con lo stesso nome all'interno di un namespace o di una dichiarazione di tipo circostante si combinano per formare una sola dichiarazione di struct, seguendo le regole specificate in §15.2.7.

16.2.5 Interfacce Struct

Una dichiarazione di struct può includere una specifica struct_interfaces , nel qual caso lo struct viene detto di implementare direttamente i tipi di interfaccia specificati. Per un tipo struct costruito, incluso un tipo annidato dichiarato all'interno di una dichiarazione di tipo generico (§15.3.9.7), ogni tipo di interfaccia implementato viene ottenuto sostituendo, per ogni type_parameter nell'interfaccia specificata, il type_argument corrispondente del tipo costruito.

struct_interfaces
    : ':' interface_type_list
    ;

La gestione delle interfacce su più parti di una dichiarazione di struct parziale (§15.2.7) è illustrata più avanti in §15.2.4.3.

Le implementazioni dell'interfaccia sono illustrate più avanti in §18.6.

Corpo Struct 16.2.6

Il struct_body di uno struct definisce i membri dello struct.

struct_body
    : '{' struct_member_declaration* '}'
    ;

16.3 Membri della Struttura

I membri di uno struct sono costituiti dai membri introdotti dai relativi struct_member_declaratione dai membri ereditati dal tipo System.ValueType.

struct_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | static_constructor_declaration
    | type_declaration
    | fixed_size_buffer_declaration   // unsafe code support
    ;

fixed_size_buffer_declaration (§23.8.2) è disponibile solo nel codice non sicuro (§23).

Nota: Tutti i tipi di class_member_declaration ad eccezione di finalizer_declaration sono anche struct_member_declaration. nota finale

Ad eccezione delle differenze indicate in §16.4, le descrizioni dei membri della classe fornite anche in §15.3 a §15.12 si applicano anche ai membri dello struct.

16.4 Differenze tra classi e struct

16.4.1 Generale

Le strutture differiscono dalle classi per vari motivi importanti.

  • Le strutture sono tipi-valore (§16.4.2).
  • Tutti i tipi di struct ereditano in modo implicito dalla classe System.ValueType (§16.4.3).
  • L'assegnazione a una variabile di un tipo struct crea una copia del valore assegnato (§16.4.4).
  • Il valore predefinito di uno struct è il valore prodotto impostando tutti i campi sul valore predefinito (§16.4.5).
  • Le operazioni di conversione boxing e unboxing vengono utilizzate per eseguire la conversione tra un tipo struct e determinati tipi di riferimento (§16.4.6).
  • Il significato di this è diverso all'interno dei membri della struct (§16.4.7).
  • Le dichiarazioni di campo dell'istanza per uno struct non possono includere inizializzatori di variabili (§16.4.8).
  • Uno struct non è autorizzato a dichiarare un costruttore di istanza senza parametri (§16.4.9).
  • Uno struct non può dichiarare un finalizzatore.

16.4.2 Semantica dei valori

Gli struct sono tipi valore (§8.3) e hanno una semantica dei valori. Le classi, d'altra parte, sono tipi di riferimento (§8.2) e hanno una semantica di riferimento.

Una variabile di un tipo struct contiene direttamente i dati dello struct, mentre una variabile di un tipo di classe contiene un riferimento a un oggetto che contiene i dati. Quando uno struct B contiene un campo di istanza di tipo A ed A è un tipo di struct, si tratta di un errore in fase di compilazione per A dipendere da B o da un tipo costruito da B. Un struct Xdipende direttamente da uno structY se X contiene un campo di istanza di tipo Y. Data questa definizione, il set completo di struct da cui un struct dipende è la chiusura transitiva della relazione dipende direttamente da.

Esempio:

struct Node
{
    int data;
    Node next; // error, Node directly depends on itself
}

è un errore perché Node contiene un campo di istanza del proprio tipo. Un altro esempio

struct A { B b; }
struct B { C c; }
struct C { A a; }

è un errore perché ognuno dei tipi A, Be C dipende l'uno dall'altro.

esempio finale

Con le classi, è possibile che due variabili facciano riferimento allo stesso oggetto e quindi sia possibile che le operazioni su una variabile influiscano sull'oggetto a cui fa riferimento l'altra variabile. Con gli struct, le variabili hanno la propria copia dei dati (tranne nel caso dei parametri di riferimento) e non è possibile che le operazioni su uno influiscano sull'altro. Inoltre, tranne quando è esplicitamente nullable (§8.3.12), non è possibile che i valori di un tipo struct siano null.

Nota: se uno struct contiene un campo di tipo riferimento, il contenuto dell'oggetto a cui viene fatto riferimento può essere modificato da altre operazioni. Tuttavia, il valore del campo stesso, ovvero l'oggetto a cui fa riferimento, non può essere modificato tramite una mutazione di un valore di struct diverso. nota finale

Esempio: dato quanto segue

struct Point
{
    public int x, y;

    public Point(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }
}

class A
{
    static void Main()
    {
        Point a = new Point(10, 10);
        Point b = a;
        a.x = 100;
        Console.WriteLine(b.x);
    }
}

l'output è 10. L'assegnazione di a a b crea una copia del valore e b pertanto non è interessata dall'assegnazione a a.x. Se Point fosse invece stato dichiarato come classe, l'output sarebbe 100 perché a e b farebbero riferimento allo stesso oggetto.

esempio finale

16.4.3 Ereditarietà

Tutti i tipi di struct ereditano in modo implicito dalla classe System.ValueType, che a sua volta eredita dalla classe object. Una dichiarazione di struct può specificare un elenco di interfacce implementate, ma non è possibile che una dichiarazione di struct specifichi una classe di base.

I tipi di struct non sono mai astratti e vengono sempre bloccati in modo implicito. I modificatori abstract e sealed pertanto non sono consentiti in una dichiarazione di struct.

Poiché l'ereditarietà non è supportata per gli struct, l'accessibilità dichiarata di un membro struct non può essere protected, private protectedo protected internal.

I membri funzione in una struttura non possono essere astratti o virtuali e il modificatore override è consentito solo per eseguire l'override dei metodi ereditati da System.ValueType.

16.4.4 Assegnazione

L'assegnazione a una variabile di un tipo struct crea una copia del valore assegnato. Ciò differisce dall'assegnazione a una variabile di un tipo di classe, che copia il riferimento ma non l'oggetto identificato dal riferimento.

Analogamente a un'assegnazione, quando uno struct viene passato come parametro di valore o restituito come risultato di un membro di funzione, viene creata una copia dello struct. Uno struct può essere passato per riferimento a un membro di funzione usando un parametro by-reference.

Quando una proprietà o un indicizzatore di uno struct è la destinazione di un'assegnazione, l'espressione di istanza associata all'accesso alla proprietà o all'indicizzatore deve essere classificata come variabile. Se l'espressione di istanza viene classificata come valore, si verifica un errore in fase di compilazione. Questo è descritto in dettaglio più dettagliatamente in §12.21.2.

16.4.5 Valori predefiniti

Come descritto in §9.3, diversi tipi di variabili vengono inizializzati automaticamente sul valore predefinito al momento della creazione. Per le variabili di tipi di classe e altri tipi di riferimento, questo valore predefinito è null. Tuttavia, poiché gli struct sono tipi valore che non possono essere null, il valore predefinito di uno struct è il valore generato impostando tutti i campi tipo valore sul valore predefinito e su tutti i campi del tipo di riferimento su null.

Esempio: riferimento allo Point struct dichiarato in precedenza, l'esempio

Point[] a = new Point[100];

inizializza ogni elemento della matrice al valore prodotto impostando i campi x e y su zero.

esempio finale

Il valore predefinito di uno struct corrisponde al valore restituito dal costruttore predefinito dello struct (§8.3.3). A differenza di una classe, uno struct non è autorizzato a dichiarare un costruttore di istanza senza parametri. Ogni struct ha invece in modo implicito un costruttore di istanza senza parametri, che restituisce sempre il valore risultante dall'impostazione di tutti i campi sui valori predefiniti.

Nota: gli struct devono essere progettati per considerare lo stato di inizializzazione predefinito uno stato valido. Nell'esempio

struct KeyValuePair
{
    string key;
    string value;

    public KeyValuePair(string key, string value)
    {
        if (key == null || value == null)
        {
            throw new ArgumentException();
        }

        this.key = key;
        this.value = value;
    }
}

Il costruttore di istanza definito dall'utente protegge contro i valori null solo quando viene chiamato esplicitamente. Nei casi in cui una KeyValuePair variabile è soggetta all'inizializzazione del valore predefinito, i campi key e value saranno null, e la struttura deve essere preparata per gestire questo stato.

nota finale

16.4.6 Boxing e unboxing

Un valore di un tipo di classe può essere convertito in tipo object o in un tipo di interfaccia implementato dalla classe semplicemente trattando il riferimento come un altro tipo in fase di compilazione. Analogamente, un valore di tipo object o un valore di un tipo di interfaccia può essere convertito nuovamente in un tipo di classe senza modificare il riferimento ( ma, naturalmente, è necessario un controllo del tipo di runtime in questo caso).

Poiché gli struct non sono tipi di riferimento, queste operazioni vengono implementate in modo diverso per i tipi di struct. Quando un valore di un tipo struct viene convertito in determinati tipi di riferimento (come definito in §10.2.9), viene eseguita un'operazione di incapsulamento (boxing). Analogamente, quando un valore di determinati tipi di riferimento (come definito in §10.3.7) viene convertito nuovamente in un tipo di struct, viene eseguita un'operazione unboxing. Una differenza fondamentale rispetto alle stesse operazioni sui tipi di classe è che le operazioni di boxing e unboxing copiano il valore dello struct nell'istanza boxed o fuori dall'istanza boxed.

Nota: Pertanto, dopo un'operazione di boxing o unboxing, le modifiche apportate all'elemento unboxed struct non vengono riflesse nell'elemento boxed struct. nota finale

Per ulteriori informazioni su boxing e unboxing, vedere §10.2.9 e §10.3.7.

16.4.7 Significato di questo

Il significato di this in uno struct è diverso dal significato di this in una classe, come descritto in §12.8.14. Quando un tipo di struct esegue l'override di un metodo virtuale ereditato da System.ValueType (ad esempio Equals, GetHashCodeo ToString), la chiamata del metodo virtuale tramite un'istanza del tipo di struct non causa l'esecuzione del boxing. Questo vale anche quando la struttura viene usata come parametro tipo e la chiamata avviene tramite un'istanza del tipo parametro.

Esempio:

struct Counter
{
    int value;
    public override string ToString() 
    {
        value++;
        return value.ToString();
    }
}

class Program
{
    static void Test<T>() where T : new()
    {
        T x = new T();
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
    }

    static void Main() => Test<Counter>();
}

L'output del programma è:

1
2
3

Anche se è cattivo stile per ToString avere effetti collaterali, l'esempio dimostra che non si è verificato alcun boxing per le tre chiamate di x.ToString().

esempio finale

Analogamente, la «boxing» non avviene mai implicitamente quando si accede a un membro di un parametro di tipo vincolato, quando tale membro è implementato nel tipo valore. Si supponga, ad esempio, che un'interfaccia ICounter contenga un metodo Increment, che può essere usato per modificare un valore. Se ICounter viene usato come vincolo, l'implementazione del metodo Increment viene chiamata con un riferimento alla variabile su cui Increment è stato chiamato, non viene mai eseguita una copia boxed.

Esempio:

interface ICounter
{
    void Increment();
}

struct Counter : ICounter
{
    int value;

    public override string ToString() => value.ToString();

    void ICounter.Increment() => value++;
}

class Program
{
    static void Test<T>() where T : ICounter, new()
    {
        T x = new T();
        Console.WriteLine(x);
        x.Increment();              // Modify x
        Console.WriteLine(x);
        ((ICounter)x).Increment();  // Modify boxed copy of x
        Console.WriteLine(x);
    }

    static void Main() => Test<Counter>();
}

La prima chiamata a Increment modifica il valore nella variabile x. Non è equivalente alla seconda chiamata a Increment, che modifica il valore in una copia incapsulata di x. Di conseguenza, l'output del programma è:

0
1
1

esempio finale

16.4.8 Inizializzatori di campo

Come descritto in §16.4.5, il valore predefinito di uno struct è costituito dal valore risultante dall'impostazione di tutti i campi di tipo valore sul valore predefinito e su tutti i campi del tipo di riferimento su null. Per questo motivo, uno struct non consente alle dichiarazioni di campo dell'istanza di includere inizializzatori di variabili. Questa restrizione si applica solo ai campi dell'istanza. I campi statici di uno struct possono includere inizializzatori di variabili.

Esempio: il codice seguente

struct Point
{
    public int x = 1; // Error, initializer not permitted
    public int y = 1; // Error, initializer not permitted
}

è in errore perché le dichiarazioni dei campi dell'istanza includono inizializzatori di variabili.

esempio finale

16.4.9 Costruttori

A differenza di una classe, uno struct non è autorizzato a dichiarare un costruttore di istanza senza parametri. Ogni struct ha invece in modo implicito un costruttore di istanza senza parametri, che restituisce sempre il valore risultante dall'impostazione di tutti i campi di tipo valore sul valore predefinito e su tutti i campi del tipo di riferimento a null (§8.3.3). Uno struct può dichiarare costruttori di istanza con parametri.

Esempio: dato quanto segue

struct Point
{
    int x, y;

    public Point(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }
}

class A
{
    static void Main()
    {
        Point p1 = new Point();
        Point p2 = new Point(0, 0);
    }
}

le istruzioni creano entrambe un Point con x e y inizializzati a zero.

esempio finale

Un costruttore di istanza di struct non è autorizzato a includere un inizializzatore del costruttore del modulo base(argument_list), dove argument_list è facoltativo.

Il this parametro di un costruttore di istanza di struct corrisponde a un parametro di output del tipo struct. Di conseguenza, this sarà attribuito definitivamente (§9.4) in ogni posizione in cui il costruttore restituisce. Analogamente, non può essere letto (anche in modo implicito) nel corpo del costruttore prima di essere assegnato definitivamente.

Se il costruttore dell'istanza dello struct specifica un inizializzatore del costruttore, tale inizializzatore viene considerato un'assegnazione definita a questo che si verifica prima del corpo del costruttore. Pertanto, il corpo stesso non ha requisiti di inizializzazione.

Esempio: considerare l'implementazione del costruttore di istanza seguente:

struct Point
{
    int x, y;

    public int X
    {
        set { x = value; }
    }

    public int Y 
    {
        set { y = value; }
    }

    public Point(int x, int y) 
    {
        X = x; // error, this is not yet definitely assigned
        Y = y; // error, this is not yet definitely assigned
    }
}

Nessun membro della funzione di istanza (incluse le funzioni di accesso set per le proprietà X e Y) può essere chiamato fino a quando non vengono assegnati tutti i campi dello struct da costruire. Si noti, tuttavia, che se Point fosse una classe anziché uno struct, l'implementazione del costruttore di istanza sarebbe consentita. C'è un'eccezione a questo e che implica proprietà implementate automaticamente (§15.7.4). Le regole di assegnazione definite (§12.21.2) esentano specificamente l'assegnazione a una proprietà automatica di un tipo struct all'interno di un costruttore di istanza di tale tipo di struct: tale assegnazione è considerata un'assegnazione definita del campo sottostante nascosto della proprietà automatica. Di conseguenza, è consentito quanto segue:

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        X = x; // allowed, definitely assigns backing field
        Y = y; // allowed, definitely assigns backing field
   }
}

esempio finale]

16.4.10 Costruttori statici

I costruttori statici per gli struct seguono la maggior parte delle stesse regole delle classi. L'esecuzione di un costruttore statico per un tipo di struct viene attivata dal primo degli eventi seguenti che si verificano all'interno di un dominio applicazione:

  • Viene fatto riferimento a un membro statico del tipo di struct.
  • Viene chiamato un costruttore esplicitamente dichiarato del tipo struct.

Nota: la creazione di valori predefiniti (§16.4.5) di tipi di struct non attiva il costruttore statico. Un esempio è il valore iniziale degli elementi in una matrice. nota finale

16.4.11 Proprietà implementate automaticamente

Le proprietà implementate automaticamente (§15.7.4) usano campi sottostanti nascosti, accessibili solo alle funzioni di accesso alle proprietà.

Nota: questa restrizione di accesso indica che i costruttori negli struct contenenti proprietà implementate automaticamente richiedono spesso un inizializzatore di costruttore esplicito in cui non ne sarebbe altrimenti necessario uno, per soddisfare il requisito di tutti i campi assegnati in modo definitivo prima che qualsiasi membro della funzione venga richiamato o il costruttore restituisca. nota finale

16.4.12 Vincolo di contesto sicuro

16.4.12.1 Generale

In fase di compilazione, ogni espressione è associata a un contesto in cui tale istanza e tutti i relativi campi possono essere accessibili in modo sicuro, il relativo contesto sicuro. Il contesto sicuro è un contesto che racchiude un'espressione in cui è sicuro per il valore essere trasferito.

Qualsiasi espressione il cui tipo in fase di compilazione non è un ref struct ha il contesto sicuro del contesto chiamante.

Un'espressione default , per qualsiasi tipo, ha un contesto sicuro di contesto del chiamante.

Per qualsiasi espressione non predefinita il cui tipo a tempo di compilazione è un ref struct, si ha un contesto sicuro definito dalle sezioni seguenti.

Il contesto sicuro registra il contesto in cui può essere copiato un valore. Data un'assegnazione da un'espressione E1 con un contesto S1sicuro a un'espressione E2 con contesto S2sicuro, si tratta di un errore se S2 è un contesto più ampio di S1.

Esistono tre valori di contesto sicuro diversi, uguali ai valori del contesto di riferimento definiti per le variabili di riferimento (§9.7.2): declaration-block, function-member e caller-context. Il contesto sicuro di un'espressione vincola l'uso come indicato di seguito:

  • Per un'istruzione return return e1, il contesto sicuro di e1 deve essere il contesto del chiamante.
  • Per un'assegnazione e1 = e2 il contesto sicuro di e2 deve essere almeno tanto ampio quanto il contesto sicuro di e1.

Per una chiamata al metodo, se è presente un ref o out argomento di un ref struct tipo (incluso il ricevitore, a meno che il tipo non sia readonly), con contesto S1 sicuro, allora nessun argomento (incluso il ricevitore) può avere un contesto sicuro più ristretto di S1.

16.4.12.2 Contesto sicuro dei parametri

Un parametro di un tipo struct ref, incluso il parametro di un metodo d'istanza, ha un contesto sicuro corrispondente al contesto del chiamante.

16.4.12.3 Contesto sicuro delle variabili locali

Una variabile locale di un tipo di struct ref ha un contesto sicuro come indicato di seguito:

  • Se la variabile è una variabile di iterazione di un foreach ciclo, il contesto sicuro della variabile corrisponde al contesto sicuro dell'espressione foreach del ciclo.
  • In caso contrario, se la dichiarazione della variabile ha un inizializzatore, il contesto sicuro della variabile corrisponde al contesto sicuro di tale inizializzatore.
  • In caso contrario, la variabile non viene inizializzata al punto di dichiarazione e ha un contesto sicuro di contesto del chiamante.

16.4.12.4 Contesto sicuro del campo

Un riferimento a un campo e.F, dove il tipo di F è un tipo di struct ref, ha un contesto sicuro uguale al contesto sicuro di e.

Operatori 16.4.12.5

L'applicazione di un operatore definito dall'utente viene considerata una chiamata al metodo (§16.4.12.6).

Per un operatore che restituisce un valore, ad esempio e1 + e2 o c ? e1 : e2, il contesto sicuro del risultato è il contesto più stretto tra i contesti sicuri degli operandi dell'operatore. Di conseguenza, per un operatore unario che restituisce un valore, ad esempio +e, il contesto sicuro del risultato è il contesto sicuro dell'operando.

Nota: il primo operando di un operatore condizionale è un bool, quindi il contesto sicuro è il contesto del chiamante. Segue che il contesto sicuro risultante è il contesto sicuro più stretto del secondo e terzo operando. nota finale

16.4.12.6 Metodo e invocazione di proprietà

Un valore risultante da una chiamata al metodo e1.M(e2, ...) o alla chiamata alla proprietà e.P ha un contesto sicuro del più piccolo dei contesti seguenti:

  • contesto del chiamante
  • Contesto sicuro di tutte le espressioni degli argomenti (incluso il ricevente).

Una chiamata di proprietà ( get o set) viene considerata una chiamata al metodo del metodo sottostante dalle regole precedenti.

16.4.12.7 stackalloc

Il risultato di un'espressione stackalloc ha un contesto sicuro di funzione-membro.

16.4.12.8 Chiamate al costruttore

Un'espressione new che richiama un costruttore rispetta le stesse regole di una chiamata al metodo considerata per restituire il tipo costruito.

Inoltre, il contesto sicuro è il più piccolo dei contesti sicuri di tutti gli argomenti e gli operandi di tutte le espressioni di inizializzatore di oggetti, se è presente un inizializzatore, in modo ricorsivo.

Nota: queste regole si basano sulla Span<T> mancata presenza di un costruttore del formato seguente:

public Span<T>(ref T p)

Tale costruttore rende le istanze di Span<T> usate come campi indistinguibili da un campo ref. Le regole di sicurezza descritte in questo documento dipendono dal ref fatto che i campi non sono un costrutto valido in C# o .NET. nota finale