Partage via


16 Structs

16.1 Général

Les structs sont similaires aux classes dans lesquelles ils représentent des structures de données qui peuvent contenir des membres de données et des membres de fonction. Toutefois, contrairement aux classes, les structs sont des types valeur et ne nécessitent pas d’allocation de tas. Une variable d’un struct type contient directement les données du struct, tandis qu’une variable d’un type de classe contient une référence aux données, ce dernier appelé objet.

Remarque : les structs sont particulièrement utiles pour les petites structures de données qui ont une sémantique de valeur. Les nombres complexes, les points dans un système de coordonnées ou les paires clé-valeur dans un dictionnaire sont de bons exemples de structures. La clé de ces structures de données est qu’elles ont peu de membres de données, qu’elles n’ont pas besoin d’utiliser la sémantique d’héritage ou de référence, plutôt qu’elles peuvent être implémentées de manière pratique à l’aide de sémantiques de valeur où l’affectation copie la valeur au lieu de la référence. Note de fin

Comme décrit dans le §8.3.5, les types simples fournis par C#, tels que int, doubleet bool, sont, en fait, tous les types de struct.

16.2 Déclarations de struct

16.2.1 Général

Un struct_declaration est un type_declaration (§14.7) qui déclare un nouveau struct :

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

Un struct_declaration se compose d’un ensemble facultatif d’attributs (§22), suivi d’un ensemble facultatif de struct_modifiers (§16.2.2), suivi d’un modificateur facultatif ref (§16.2.3), suivi d’un modificateur partiel facultatif (§15.2.7), suivi du mot clé et d’un struct qui nomme le struct, suivi d’une spécification facultative type_parameter_list (§15.2.3)), suivi d’une spécification struct_interfaces facultative (§16.2.5), suivie d’une spécification facultative type_parameter_constraints-clauses (§15.2.5), suivie d’une struct_body (§16.2.6), éventuellement suivie d’un point-virgule.

Une déclaration de struct ne fournit pas de type_parameter_constraints_clauses sauf s’il fournit également une type_parameter_list.

Une déclaration de struct qui fournit une type_parameter_list est une déclaration de struct générique. En outre, tout struct imbriqué à l’intérieur d’une déclaration de classe générique ou d’une déclaration de struct générique est lui-même une déclaration de struct générique, car les arguments de type pour le type conteneur doivent être fournis pour créer un type construit (§8.4).

Une déclaration de struct qui inclut un ref mot clé n’a pas de partie struct_interfaces .

Modificateurs de struct 16.2.2

Un struct_declaration peut éventuellement inclure une séquence de struct_modifiers :

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

unsafe_modifier (§23.2) est disponible uniquement dans le code non sécurisé (§23).

Il s’agit d’une erreur au moment de la compilation pour que le même modificateur apparaisse plusieurs fois dans une déclaration de struct.

readonlyÀ l’exception de , les modificateurs d’une déclaration de struct ont la même signification que ceux d’une déclaration de classe (§15.2.2).

Le readonly modificateur indique que le struct_declaration déclare un type dont les instances sont immuables.

Un struct en lecture seule a les contraintes suivantes :

  • Chacun de ses champs d’instance doit également être déclaré readonly.
  • Aucune de ses propriétés d’instance ne doit avoir de set_accessor_declaration (§15.7.3).
  • Il ne doit déclarer aucun événement de type champ (§15.8.2).

Lorsqu’une instance d’un struct en lecture seule est passée à une méthode, elle this est traitée comme un argument/paramètre d’entrée, ce qui interdit l’accès en écriture à tous les champs d’instance (sauf par les constructeurs).

16.2.3 Modificateur Ref

Le ref modificateur indique que le struct_declaration déclare un type dont les instances sont allouées sur la pile d’exécution. Ces types sont appelés types de struct ref. Le ref modificateur déclare que les instances peuvent contenir des champs de type ref et ne doivent pas être copiées hors de son contexte sécurisé (§16.4.12). Les règles permettant de déterminer le contexte sûr d’un struct ref sont décrites dans le §16.4.12.

Il s’agit d’une erreur au moment de la compilation si un type de struct ref est utilisé dans l’un des contextes suivants :

  • En tant que type d’élément d’un tableau.
  • Comme type déclaré d’un champ d’une classe ou d’un struct qui n’a pas le ref modificateur.
  • Être boxé à System.ValueType ou System.Object.
  • En tant qu’argument de type.
  • En tant que type d’élément tuple.
  • Méthode asynchrone.
  • Itérateur.
  • Il n’existe aucune conversion d’un ref struct type vers le type object ou le type System.ValueType.
  • Un ref struct type ne doit pas être déclaré pour implémenter une interface.
  • Une méthode d’instance déclarée dans object ou in System.ValueType , mais non substituée dans un ref struct type, ne doit pas être appelée avec un récepteur de ce ref struct type.
  • Une méthode d’instance d’un ref struct type ne doit pas être capturée par la conversion de groupe de méthodes en type délégué.
  • Un struct ref ne doit pas être capturé par une expression lambda ou une fonction locale.

Remarque : A ref struct ne doit pas déclarer async les méthodes d’instance ni utiliser une yield return instruction yield break dans une méthode d’instance, car le paramètre implicite this ne peut pas être utilisé dans ces contextes. Note de fin

Ces contraintes garantissent qu’une variable de type ne fait pas référence à la mémoire de ref struct pile qui n’est plus valide ou aux variables qui ne sont plus valides.

16.2.4 Modificateur partiel

Le partial modificateur indique que cette struct_declaration est une déclaration de type partielle. Plusieurs déclarations de struct partielles portant le même nom dans un espace de noms englobant ou une déclaration de type se combinent pour former une déclaration de struct, en suivant les règles spécifiées dans le §15.2.7.

Interfaces struct 16.2.5

Une déclaration de struct peut inclure une spécification struct_interfaces , auquel cas le struct est dit pour implémenter directement les types d’interface donnés. Pour un type de struct construit, y compris un type imbriqué déclaré dans une déclaration de type générique (§15.3.9.7), chaque type d’interface implémenté est obtenu en remplaçant, pour chaque type_parameter dans l’interface donnée, le type_argument correspondant du type construit.

struct_interfaces
    : ':' interface_type_list
    ;

La gestion des interfaces sur plusieurs parties d’une déclaration partielle de struct (§15.2.7) est abordée plus loin dans le §15.2.4.3.

Les implémentations d’interface sont abordées plus loin dans le §18.6.

16.2.6 Corps de struct

La struct_body d’un struct définit les membres du struct.

struct_body
    : '{' struct_member_declaration* '}'
    ;

16.3 Membres du struct

Les membres d’un struct se composent des membres introduits par ses struct_member_declarations et les membres hérités du type 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) est disponible uniquement dans le code non sécurisé (§23).

Remarque : Toutes sortes de class_member_declarations, sauf finalizer_declaration sont également struct_member_declarations. Note de fin

À l’exception des différences indiquées dans le §16.4, les descriptions des membres de classe fournis dans le §15.3 et le §15.12 s’appliquent également aux membres du struct.

Différences de classe et de struct 16.4

16.4.1 Général

Les structs diffèrent des classes de plusieurs façons importantes :

  • Les structs sont des types valeur (§16.4.2).
  • Tous les types de struct héritent implicitement de la classe System.ValueType (§16.4.3).
  • L’affectation à une variable d’un type de struct crée une copie de la valeur affectée (§16.4.4).
  • La valeur par défaut d’un struct est la valeur produite en définissant tous les champs sur leur valeur par défaut (§16.4.5).
  • Les opérations boxing et unboxing sont utilisées pour effectuer une conversion entre un type de struct et certains types de référence (§16.4.6).
  • La signification est this différente dans les membres du struct (§16.4.7).
  • Les déclarations de champ d’instance d’un struct ne sont pas autorisées à inclure des initialiseurs de variables (§16.4.8).
  • Un struct n’est pas autorisé à déclarer un constructeur d’instance sans paramètre (§16.4.9).
  • Un struct n’est pas autorisé à déclarer un finaliseur.

Sémantique de valeur 16.4.2

Les structs sont des types valeur (§8.3) et sont dits avoir une sémantique de valeur. Les classes, d’autre part, sont des types de référence (§8.2) et sont dits avoir une sémantique de référence.

Une variable d’un type de struct contient directement les données du struct, tandis qu’une variable d’un type de classe contient une référence à un objet qui contient les données. Lorsqu’un struct B contient un champ d’instance de type A et A qu’il s’agit d’un type de struct, il s’agit d’une erreur au moment de la compilation pour A dépendre B ou d’un type construit à partir de B. Un struct Xdépend directement de d’un struct Y si X contient un champ d’instance de type Y. Étant donné cette définition, l’ensemble complet de structs sur lesquels dépend un struct est la fermeture transitive de la relation directe .

Exemple :

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

est une erreur, car Node contient un champ d’instance de son propre type. Autre exemple :

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

est une erreur, car chacun des types A, Bet C dépend les uns des autres.

exemple de fin

Avec les classes, il est possible que deux variables référencent le même objet, et ainsi que les opérations sur une variable affectent l’objet référencé par l’autre variable. Avec les structs, les variables ont chacune leur propre copie des données (sauf dans le cas de paramètres de référence) et il n’est pas possible d’effectuer des opérations sur l’une d’elles pour affecter l’autre. En outre, sauf lorsqu’il est explicitement nullable (§8.3.12), il n’est pas possible que les valeurs d’un type de struct soient null.

Remarque : si un struct contient un champ de type référence, le contenu de l’objet référencé peut être modifié par d’autres opérations. Toutefois, la valeur du champ lui-même, c’est-à-dire l’objet auquel il fait référence, ne peut pas être modifiée par une mutation d’une valeur de struct différente. Note de fin

Exemple : compte tenu des éléments suivants

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

la sortie est 10. Affectation de a créer b une copie de la valeur et b n’est donc pas affectée par l’affectation à a.x. Au Point lieu de cela, la sortie aurait 100 été déclarée en tant que classe, car a et b référencerait le même objet.

exemple de fin

Héritage 16.4.3

Tous les types de struct héritent implicitement de la classe System.ValueType, qui hérite à son tour de la classe object. Une déclaration de struct peut spécifier une liste d’interfaces implémentées, mais il n’est pas possible qu’une déclaration de struct spécifie une classe de base.

Les types de structs ne sont jamais abstraits et sont toujours implicitement scellés. Les abstract modificateurs et sealed les modificateurs ne sont donc pas autorisés dans une déclaration de struct.

Étant donné que l’héritage n’est pas pris en charge pour les structs, l’accessibilité déclarée d’un membre de struct ne peut pas être protected, private protectedou protected internal.

Les membres de fonction d’un struct ne peuvent pas être abstraits ou virtuels, et le override modificateur est autorisé uniquement à remplacer les méthodes héritées de System.ValueType.

16.4.4 Affectation

L’affectation à une variable d’un type de struct crée une copie de la valeur affectée. Cela diffère de l’affectation à une variable d’un type de classe, qui copie la référence, mais pas l’objet identifié par la référence.

Comme pour une affectation, lorsqu’un struct est passé en tant que paramètre de valeur ou retourné à la suite d’un membre de fonction, une copie du struct est créée. Un struct peut être passé par référence à un membre de fonction à l’aide d’un paramètre de référence.

Lorsqu’une propriété ou un indexeur d’un struct est la cible d’une affectation, l’expression d’instance associée à l’accès à la propriété ou à l’indexeur doit être classifiée comme variable. Si l’expression d’instance est classifiée comme valeur, une erreur au moment de la compilation se produit. Ceci est décrit plus en détail dans le §12.21.2.

16.4.5 Valeurs par défaut

Comme décrit dans le §9.3, plusieurs types de variables sont automatiquement initialisés à leur valeur par défaut lorsqu’elles sont créées. Pour les variables des types de classes et d’autres types de référence, cette valeur par défaut est null. Toutefois, étant donné que les structs sont des types valeur qui ne peuvent pas être null, la valeur par défaut d’un struct est la valeur produite en définissant tous les champs de type valeur sur leur valeur par défaut et tous les champs de type référence sur null.

Exemple : référence au Point struct déclaré ci-dessus, l’exemple

Point[] a = new Point[100];

initialise chacun Point dans le tableau à la valeur produite en définissant les champs et x les y champs sur zéro.

exemple de fin

La valeur par défaut d’un struct correspond à la valeur retournée par le constructeur par défaut du struct (§8.3.3). Contrairement à une classe, un struct n’est pas autorisé à déclarer un constructeur d’instance sans paramètre. Au lieu de cela, chaque struct a implicitement un constructeur d’instance sans paramètre, qui retourne toujours la valeur qui résulte de la définition de tous les champs à leurs valeurs par défaut.

Remarque : les structs doivent être conçus pour prendre en compte l’état d’initialisation par défaut un état valide. Dans l’exemple

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

le constructeur d’instance défini par l’utilisateur protège contre null les valeurs uniquement lorsqu’il est explicitement appelé. Dans les cas où une variable est soumise à l’initialisation KeyValuePair de valeur par défaut, les champs et key les value champs seront null, et le struct doit être prêt à gérer cet état.

Note de fin

16.4.6 Boxing et unboxing

Une valeur d’un type de classe peut être convertie en type object ou en type d’interface implémentée par la classe simplement en traitant la référence comme un autre type au moment de la compilation. De même, une valeur de type object ou une valeur d’un type d’interface peut être convertie en type de classe sans modifier la référence (mais bien sûr, une vérification de type d’exécution est requise dans ce cas).

Étant donné que les structs ne sont pas des types de référence, ces opérations sont implémentées différemment pour les types de struct. Lorsqu’une valeur d’un type de struct est convertie en certains types de référence (comme défini dans le §10.2.9), une opération de boxe a lieu. De même, lorsqu’une valeur de certains types de référence (telle que définie dans le §10.3.7) est convertie en type de struct, une opération d’annulation de boîte de réception a lieu. Une différence clé par rapport aux mêmes opérations sur les types de classes est que boxing et unboxing copie la valeur de struct dans ou hors de l’instance boxed.

Remarque : Par conséquent, en suivant une opération de boxe ou de déséboxage, les modifications apportées à l’unboxed struct ne sont pas reflétées dans le boxed struct. Note de fin

Pour plus d’informations sur la boxe et le déboxing, consultez §10.2.9 et §10.3.7.

16.4.7 Signification de ce

La signification d’un this struct diffère de la signification d’une this classe, comme décrit dans le §12.8.14. Lorsqu’un type de struct substitue une méthode virtuelle héritée de System.ValueType (par Equalsexemple, , GetHashCodeou ToString), l’appel de la méthode virtuelle via une instance du type de struct n’entraîne pas de boxing. Cela est vrai même lorsque le struct est utilisé comme paramètre de type et que l’appel se produit par le biais d’une instance du type de paramètre de type.

Exemple :

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>();
}

La sortie du programme est la suivante :

1
2
3

Bien qu’il soit mauvais pour ToString avoir des effets secondaires, l’exemple montre qu’aucune boxe n’a eu lieu pour les trois appels de x.ToString().

exemple de fin

De même, la boxe ne se produit jamais implicitement lors de l’accès à un membre sur un paramètre de type contraint lorsque le membre est implémenté dans le type valeur. Par exemple, supposons qu’une interface ICounter contient une méthode Increment, qui peut être utilisée pour modifier une valeur. Si ICounter elle est utilisée comme contrainte, l’implémentation de la Increment méthode est appelée avec une référence à la variable appelée Increment , jamais une copie boxée.

Exemple :

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>();
}

Le premier appel pour Increment modifier la valeur dans la variable x. Cela n’équivaut pas au deuxième appel à Increment, qui modifie la valeur dans une copie boxée de x. Ainsi, la sortie du programme est la suivante :

0
1
1

exemple de fin

16.4.8 Initialiseurs de champ

Comme décrit dans le §16.4.5, la valeur par défaut d’un struct se compose de la valeur qui résulte de la définition de tous les champs de type valeur sur leur valeur par défaut et de tous les champs de type référence sur null. Pour cette raison, un struct n’autorise pas les déclarations de champ d’instance à inclure des initialiseurs de variables. Cette restriction s’applique uniquement aux champs d’instance. Les champs statiques d’un struct sont autorisés à inclure des initialiseurs de variables.

Exemple : les éléments suivants

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

est en erreur, car les déclarations de champ d’instance incluent des initialiseurs de variables.

exemple de fin

16.4.9 Constructeurs

Contrairement à une classe, un struct n’est pas autorisé à déclarer un constructeur d’instance sans paramètre. Au lieu de cela, chaque struct a implicitement un constructeur d’instance sans paramètre, qui retourne toujours la valeur qui résulte de la définition de tous les champs de type valeur à leur valeur par défaut et de tous les champs de type référence sur null (§8.3.3). Un struct peut déclarer des constructeurs d’instance ayant des paramètres.

Exemple : compte tenu des éléments suivants

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

les instructions créent une Point instruction avec x et y initialisée à zéro.

exemple de fin

Un constructeur d’instance de struct n’est pas autorisé à inclure un initialiseur de constructeur du formulaire base(argument_list), où argument_list est facultatif.

Le this paramètre d’un constructeur d’instance de struct correspond à un paramètre de sortie du type de struct. Par conséquent, this doit être définitivement affecté (§9.4) à chaque emplacement où le constructeur retourne. De même, il ne peut pas être lu (même implicitement) dans le corps du constructeur avant d’être définitivement affecté.

Si le constructeur d’instance de struct spécifie un initialiseur de constructeur, cet initialiseur est considéré comme une affectation définitive à ceci qui se produit avant le corps du constructeur. Par conséquent, le corps lui-même n’a pas d’exigences d’initialisation.

Exemple : Considérez l’implémentation du constructeur d’instance ci-dessous :

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

Aucun membre de fonction d’instance (y compris les accesseurs set pour les propriétés X et Y) ne peut être appelé tant que tous les champs du struct en cours de construction n’ont pas été définitivement affectés. Notez toutefois que, s’il Point s’agissait d’une classe au lieu d’un struct, l’implémentation du constructeur d’instance serait autorisée. Il existe une exception à cela, et cela implique des propriétés implémentées automatiquement (§15.7.4). Les règles d’affectation définies (§12.21.2) exemptent spécifiquement l’affectation à une propriété automatique d’un type de struct au sein d’un constructeur d’instance de ce type de struct : une telle affectation est considérée comme une affectation définitive du champ de stockage masqué de la propriété automatique. Ainsi, les éléments suivants sont autorisés :

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

exemple de fin]

16.4.10 Constructeurs statiques

Les constructeurs statiques pour les structs suivent la plupart des mêmes règles que pour les classes. L’exécution d’un constructeur statique pour un type de struct est déclenchée par le premier des événements suivants à exécuter dans un domaine d’application :

  • Un membre statique du type de struct est référencé.
  • Un constructeur déclaré explicitement du type de struct est appelé.

Remarque : La création de valeurs par défaut (§16.4.5) de types de struct ne déclenche pas le constructeur statique. (Par exemple, il s’agit de la valeur initiale des éléments d’un tableau.) Note de fin

16.4.11 Implémentation automatique des propriétés

Les propriétés implémentées automatiquement (§15.7.4) utilisent des champs de stockage masqués, qui sont accessibles uniquement aux accesseurs de propriétés.

Remarque : Cette restriction d’accès signifie que les constructeurs dans les structs contenant des propriétés implémentées automatiquement ont souvent besoin d’un initialiseur de constructeur explicite où ils n’en auraient pas besoin autrement, pour satisfaire à l’exigence de tous les champs affectés définitivement avant qu’un membre de fonction soit appelé ou que le constructeur retourne. Note de fin

16.4.12 Contrainte de contexte sécurisé

16.4.12.1 Général

Au moment de la compilation, chaque expression est associée à un contexte dans lequel cette instance et tous ses champs sont accessibles en toute sécurité, son contexte sécurisé. Le contexte sécurisé est un contexte englobant une expression dans laquelle il est sûr que la valeur s’échappe.

Toute expression dont le type au moment de la compilation n’est pas un struct ref a un contexte sécurisé d’appelant-contexte.

Une default expression, pour n’importe quel type, a un contexte sécurisé d’appelant-contexte.

Pour toute expression non par défaut dont le type au moment de la compilation est un struct ref a un contexte sécurisé défini par les sections suivantes.

Enregistrements de contexte sécurisé dans lesquels une valeur peut être copiée. Étant donné une affectation d’une expression E1 avec un contexte S1sécurisé , à une expression E2 avec un contexte S2sécurisé , il s’agit d’une erreur si S2 c’est un contexte plus large que S1.

Il existe trois valeurs de contexte sécurisé différentes, identiques aux valeurs de contexte ref-safe définies pour les variables de référence (§9.7.2) : le bloc de déclaration, le membre de fonction et le contexte de l’appelant. Le contexte sécurisé d’une expression limite son utilisation comme suit :

  • Pour une instruction return e1de retour, le contexte sécurisé de e1 doit être caller-context.
  • Pour une affectation e1 = e2 , le contexte sûr de e2 doit être au moins aussi large qu’un contexte sûr de e1.

Pour un appel de méthode s’il existe un ref ou out un argument d’un ref struct type (y compris le récepteur, sauf si le type est readonly), avec un contexte S1sécurisé , aucun argument (y compris le récepteur) peut avoir un contexte sécurisé plus étroit que S1.

16.4.12.2 Contexte sécurisé du paramètre

Un paramètre d’un type de struct ref, y compris le this paramètre d’une méthode d’instance, a un contexte sécurisé d’appelant-contexte.

16.4.12.3 Contexte sécurisé de variable locale

Une variable locale d’un type de struct ref a un contexte sécurisé comme suit :

  • Si la variable est une variable d’itération d’une foreach boucle, le contexte sécurisé de la variable est identique au contexte sécurisé de l’expression de la foreach boucle.
  • Sinon, si la déclaration de la variable a un initialiseur, le contexte sécurisé de la variable est identique au contexte sécurisé de cet initialiseur.
  • Sinon, la variable n’est pas initialisée au point de déclaration et a un contexte sécurisé d’appelant-contexte.

16.4.12.4 Contexte sécurisé du champ

Une référence à un champ e.F, où le type de F type est un type de struct ref, a un contexte sécurisé qui est identique au contexte sécurisé de e.

16.4.12.5 Opérateurs

L’application d’un opérateur défini par l’utilisateur est traitée comme un appel de méthode (§16.4.12.6).

Pour un opérateur qui génère une valeur, par exemple e1 + e2 , c ? e1 : e2le contexte sécurisé du résultat est le contexte le plus étroit parmi les contextes sécurisés des opérandes de l’opérateur. Par conséquent, pour un opérateur unaire qui génère une valeur, par +eexemple, le contexte sécurisé du résultat est le contexte sécurisé de l’opérande.

Remarque : Le premier opérande d’un opérateur conditionnel est un bool, de sorte que son contexte sécurisé est le contexte appelant. Il suit que le contexte sécurisé résultant est le contexte sécurisé le plus étroit du deuxième et du troisième opérande. Note de fin

16.4.12.6 Méthode et appel de propriété

Une valeur résultant d’un appel de méthode ou d’un appel e1.M(e2, ...)e.P de propriété a un contexte sécurisé du plus petit des contextes suivants :

  • caller-context.
  • Contexte sécurisé de toutes les expressions d’argument (y compris le récepteur).

Un appel de propriété (ou getset) est traité comme un appel de méthode de la méthode sous-jacente par les règles ci-dessus.

16.4.12.7 stackalloc

Le résultat d’une expression stackalloc a un contexte sécurisé de membre de fonction.

16.4.12.8 Appels de constructeur

Expression new qui appelle un constructeur obéit aux mêmes règles qu’un appel de méthode qui est considéré comme renvoyant le type construit.

En outre, le contexte sécurisé est le plus petit des contextes sécurisés de tous les arguments et opérandes de toutes les expressions d’initialiseur d’objet, de manière récursive, si un initialiseur est présent.

Remarque : Ces règles s’appuient sur Span<T> l’échec d’un constructeur du formulaire suivant :

public Span<T>(ref T p)

Un tel constructeur rend les instances utilisées Span<T> en tant que champs indistinguishables à partir d’un ref champ. Les règles de sécurité décrites dans ce document dépendent des ref champs qui ne sont pas une construction valide en C# ou .NET. Note de fin