Condividi tramite


16 Struct

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_modifiers (§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 di type_parameter_list facoltativa (§15.2.3), seguito da una specifica facoltativa di struct_interfaces (§16.2.5), seguita da una specifica facoltativa di type_parameter_constraints clausole (§15.2.5), seguita da un struct_body (§16.2.6), facoltativamente seguito da un punto e virgola.

Una dichiarazione di struct non fornisce un type_parameter_constraints_clauses a meno che non fornisca 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 parola chiave 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).

Si tratta di un errore in fase di compilazione affinché lo stesso modificatore venga visualizzato 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 readonlyanche .
  • Nessuna delle relative proprietà di istanza deve avere un set_accessor_declaration (§15.7.3).
  • Non deve dichiarare eventi simili a campi (§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 denominati tipi di struct ref. Il ref modificatore dichiara che le istanze possono contenere campi simili ai riferimenti e non devono essere copiate dal 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 sottoposto a boxing in System.ValueType o System.Object.
  • Come argomento di tipo.
  • Come tipo di un elemento di 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 o in object System.ValueType ma non sottoposto a override in un ref struct tipo non deve essere chiamato con un ricevitore di tale ref struct tipo.
  • Un metodo di istanza di un ref struct tipo non deve essere acquisito 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 di struct parziali con lo stesso nome all'interno di una dichiarazione di tipo o spazio dei nomi di inclusione combinano per formare una dichiarazione di struct, seguendo le regole specificate in §15.2.7.

Interfacce struct 16.2.5

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 Struct

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: anche tutti i tipi di class_member_declarationad eccezione di finalizer_declaration sono 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

Gli struct differiscono dalle classi in diversi modi importanti:

  • Gli struct 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 è diverso all'interno dei this membri dello 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 è autorizzato a dichiarare un finalizzatore.

16.4.2 Semantica dei valori

Gli struct sono tipi valore (§8.3) e hanno una semantica di 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. A struct Xdipende direttamente da uno struct Y se X contiene un campo di istanza di tipo Y. Data questa definizione, il set completo di struct da cui dipende uno struct è la chiusura transitiva dell'oggetto direttamente dipende dalla relazione.

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 invece fosse stata dichiarata come classe, l'output sarebbe dovuto al 100 fatto a che 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 abstract modificatori e sealed non sono pertanto 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 della funzione in uno struct non possono essere astratti o virtuali e il modificatore può eseguire l'override 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 Point oggetto della matrice sul valore prodotto impostando i x campi 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 dell'istanza definita dall'utente protegge dai null valori solo in cui viene chiamato in modo esplicito. Nei casi in cui una KeyValuePair variabile è soggetta all'inizializzazione del valore predefinito, i key campi e value saranno nulle lo struct deve essere preparato 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 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 la conversione boxing e unboxing copia il valore dello struct nell'istanza boxed o out of the boxed.

Nota: pertanto, dopo un'operazione di boxing o unboxing, le modifiche apportate alla conversione unboxing non vengono riflesse nel boxed struct struct. nota finale

Per altri dettagli sulla conversione 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 lo struct viene usato come parametro di tipo e la chiamata avviene tramite un'istanza del tipo di parametro di tipo.

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 è uno stile non valido 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 conversione boxing non si verifica mai in modo implicito quando si accede a un membro in un parametro di tipo vincolato quando il membro viene implementato all'interno del 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 Increment metodo viene chiamata con un riferimento alla variabile chiamata Increment su, 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 equivale alla seconda chiamata a Increment, che modifica il valore in una copia boxed 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 un Point oggetto con x e y inizializzato su 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à sicuramente assegnato (§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 dichiarato in modo esplicito del tipo di 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, che è sicura per il valore in cui eseguire l'escape.

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

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

Per qualsiasi espressione non predefinita il cui tipo di compilazione è uno struct ref 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 e1return , il contesto sicuro di deve essere il contesto del e1 chiamante.
  • Per un'assegnazione e1 = e2 il contesto sicuro di e2 deve essere almeno un contesto ampio come contesto sicuro di e1.

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

16.4.12.2 Contesto sicuro dei parametri

Un parametro di un tipo di struct ref, incluso il this parametro di un metodo di istanza, ha un contesto sicuro di 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 chiamata di proprietà

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

  • contesto chiamante.
  • Contesto sicuro di tutte le espressioni di argomento (incluso il ricevitore).

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 membro funzione.

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, in modo ricorsivo, se è presente un inizializzatore.

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 indistinguish da un ref campo. 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