Compartir a través de


16 Structs

16.1 General

Las estructuras son similares a las clases de que representan estructuras de datos que pueden contener miembros de datos y miembros de función. Sin embargo, a diferencia de las clases, las estructuras son tipos de valor y no requieren asignación de montón. Una variable de un struct tipo contiene directamente los datos de struct, mientras que una variable de un tipo de clase contiene una referencia a los datos, el último conocido como objeto .

Nota: Las estructuras son especialmente útiles para estructuras de datos pequeñas que tienen semántica de valor. Los números complejos, los puntos de un sistema de coordenadas o los pares clave-valor de un diccionario son buenos ejemplos de structs. La clave de estas estructuras de datos es que tienen pocos miembros de datos, que no requieren el uso de la semántica de herencia o referencia, sino que se pueden implementar convenientemente mediante la semántica de valores donde la asignación copia el valor en lugar de la referencia. nota final

Como se describe en §8.3.5, los tipos simples proporcionados por C#, como int, doubley bool, son, de hecho, todos los tipos de estructura.

16.2 Declaraciones de estructura

16.2.1 General

Un struct_declaration es un type_declaration (§14.7) que declara una nueva estructura:

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

Un struct_declaration consta de un conjunto opcional de atributos (§22), seguido de un conjunto opcional de struct_modifiers (§16.2.2), seguido de un modificador opcional ref (§16.2.3), seguido de un modificador parcial opcional (§15.2.7), seguido de la palabra clave struct y un identificador que asigna el nombre a la estructura, seguido de una especificación de type_parameter_list opcional (§15.2.3)), seguido de una especificación opcional de struct_interfaces (§16.2.5), seguida de una especificación opcional de cláusulas type_parameter_constraints (§15.2.5), seguida de un struct_body (§16.2.6), opcionalmente seguido de un punto y coma.

Una declaración de estructura no proporcionará una type_parameter_constraints_clauses a menos que también proporcione una type_parameter_list.

Una declaración de estructura que proporciona un type_parameter_list es una declaración de estructura genérica. Además, cualquier estructura anidada dentro de una declaración de clase genérica o una declaración de estructura genérica es una declaración de estructura genérica, ya que se proporcionarán argumentos de tipo para el tipo contenedor para crear un tipo construido (§8.4).

Una declaración de estructura que incluya una ref palabra clave no tendrá una parte struct_interfaces .

Modificadores Struct 16.2.2

Un struct_declaration puede incluir opcionalmente una secuencia de struct_modifiers:

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

unsafe_modifier (§23.2) solo está disponible en código no seguro (§23).

Es un error en tiempo de compilación para que el mismo modificador aparezca varias veces en una declaración de estructura.

Excepto para readonly, los modificadores de una declaración de estructura tienen el mismo significado que los de una declaración de clase (§15.2.2).

El readonly modificador indica que el struct_declaration declara un tipo cuyas instancias son inmutables.

Una estructura de solo lectura tiene las siguientes restricciones:

  • Cada uno de sus campos de instancia también se declarará readonly.
  • Ninguna de sus propiedades de instancia tendrá un set_accessor_declaration (§15.7.3).
  • No declarará ningún evento similar a campo (§15.8.2).

Cuando una instancia de una estructura de solo lectura se pasa a un método, se this trata como un argumento o parámetro de entrada, que no permite el acceso de escritura a cualquier campo de instancia (excepto por constructores).

Modificador Ref 16.2.3 Ref

El ref modificador indica que el struct_declaration declara un tipo cuyas instancias se asignan en la pila de ejecución. Estos tipos se denominan tipos de estructura ref. El ref modificador declara que las instancias pueden contener campos de tipo ref y no se copiarán fuera de su contexto seguro (§16.4.12). Las reglas para determinar el contexto seguro de una estructura ref se describen en §16.4.12.

Se trata de un error en tiempo de compilación si se usa un tipo de estructura ref en cualquiera de los contextos siguientes:

  • Como tipo de elemento de una matriz.
  • Como el tipo declarado de un campo de una clase o una estructura que no tiene el ref modificador .
  • La conversión boxeada en System.ValueType o System.Object.
  • Como argumento de tipo.
  • Como el tipo de un elemento de tupla.
  • Un método asincrónico.
  • Iterador.
  • No hay ninguna conversión de un ref struct tipo al tipo object o al tipo System.ValueType.
  • No ref struct se declarará un tipo para implementar ninguna interfaz.
  • No se llamará a un método de instancia declarado en object o en pero System.ValueType no invalidado en un ref struct tipo con un receptor de ese ref struct tipo.
  • Un método de instancia de un ref struct tipo no se capturará mediante la conversión de grupo de métodos a un tipo delegado.
  • Una estructura ref no se capturará mediante una expresión lambda ni una función local.

Nota: Un ref struct no declarará async métodos de instancia ni usará una yield return instrucción o yield break dentro de un método de instancia, ya que el parámetro implícito this no se puede usar en esos contextos. nota final

Estas restricciones garantizan que una variable de ref struct tipo no haga referencia a la memoria de pila que ya no sea válida o a variables que ya no sean válidas.

16.2.4 Modificador parcial

El partial modificador indica que este struct_declaration es una declaración de tipo parcial. Varias declaraciones de estructura parciales con el mismo nombre dentro de un espacio de nombres o declaración de tipo envolvente se combinan para formar una declaración de estructura, siguiendo las reglas especificadas en §15.2.7.

Interfaces struct de 16.2.5

Una declaración de estructura puede incluir una especificación struct_interfaces , en cuyo caso se dice que la estructura implementa directamente los tipos de interfaz especificados. Para un tipo de estructura construido, incluido un tipo anidado declarado dentro de una declaración de tipo genérico (§15.3.9.7), cada tipo de interfaz implementado se obtiene sustituyendo, por cada type_parameter en la interfaz especificada, el type_argument correspondiente del tipo construido.

struct_interfaces
    : ':' interface_type_list
    ;

El control de interfaces en varias partes de una declaración de estructura parcial (§15.2.7) se describe más adelante en §15.2.4.3.

Las implementaciones de interfaz se describen más adelante en §18.6.

Cuerpo de estructura 16.2.6

El struct_body de un struct define los miembros de la estructura.

struct_body
    : '{' struct_member_declaration* '}'
    ;

16.3 Miembros de Struct

Los miembros de una estructura constan de los miembros introducidos por sus struct_member_declarations y los miembros heredados del 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) solo está disponible en código no seguro (§23).

Nota: Todos los tipos de class_member_declarationexcepto finalizer_declaration también se struct_member_declarations. nota final

A excepción de las diferencias indicadas en §16.4, las descripciones de los miembros de clase proporcionados en §15.3 a §15.12 también se aplican a los miembros struct.

16.4 Diferencias de clase y estructura

16.4.1 General

Las estructuras difieren de las clases de varias maneras importantes:

  • Las estructuras son tipos de valor (§16.4.2).
  • Todos los tipos de estructura heredan implícitamente de la clase System.ValueType (§16.4.3).
  • La asignación a una variable de un tipo de estructura crea una copia del valor que se asigna (§16.4.4).
  • El valor predeterminado de un struct es el valor generado estableciendo todos los campos en su valor predeterminado (§16.4.5).
  • Las operaciones de conversión boxing y unboxing se usan para convertir entre un tipo de estructura y determinados tipos de referencia (§16.4.6).
  • El significado de es diferente dentro de this los miembros de estructura (§16.4.7).
  • Las declaraciones de campo de instancia de un struct no pueden incluir inicializadores de variables (§16.4.8).
  • No se permite que un struct declare un constructor de instancia sin parámetros (§16.4.9).
  • No se permite que un struct declare un finalizador.

16.4.2 Semántica de valores

Las estructuras son tipos de valor (§8.3) y se dice que tienen semántica de valor. Las clases, por otro lado, son tipos de referencia (§8.2) y se dice que tienen semántica de referencia.

Una variable de un tipo de estructura contiene directamente los datos de la estructura, mientras que una variable de un tipo de clase contiene una referencia a un objeto que contiene los datos. Cuando un struct B contiene un campo de instancia de tipo A y A es un tipo de estructura, es un error en tiempo de compilación para A que dependa B de o de un tipo construido a partir de B. A struct Xdepende directamente de una estructura Y si X contiene un campo de instancia de tipo Y. Dada esta definición, el conjunto completo de structs sobre los que depende una estructura es el cierre transitivo del elemento directamente depende de la relación.

Ejemplo:

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

es un error porque Node contiene un campo de instancia de su propio tipo. Otro ejemplo

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

es un error porque cada uno de los tipos A, By C dependen entre sí.

ejemplo final

Con las clases, es posible que dos variables hagan referencia al mismo objeto y, por tanto, es posible que las operaciones de una variable afecten al objeto al que hace referencia la otra variable. Con structs, las variables tienen su propia copia de los datos (excepto en el caso de los parámetros by-reference) y no es posible que las operaciones de una afecten a la otra. Además, excepto cuando se aceptan valores NULL explícitamente (§8.3.12), no es posible que los valores de un tipo de estructura sean null.

Nota: Si una estructura contiene un campo de tipo de referencia, otras operaciones pueden modificar el contenido del objeto al que se hace referencia. Sin embargo, el valor del propio campo, es decir, el objeto al que hace referencia, no se puede cambiar a través de una mutación de un valor de estructura diferente. nota final

Ejemplo: dado lo siguiente

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 salida es 10. La asignación de a para b crea una copia del valor y b , por tanto, no se ve afectada por la asignación a a.x. En su lugar, se había Point declarado como una clase, la salida sería 100 porque a y b haría referencia al mismo objeto.

ejemplo final

16.4.3 Herencia

Todos los tipos de estructura heredan implícitamente de la clase System.ValueType, que, a su vez, hereda de la clase object. Una declaración de estructura puede especificar una lista de interfaces implementadas, pero no es posible que una declaración de estructura especifique una clase base.

Los tipos de estructura nunca son abstractos y siempre están sellados implícitamente. Por lo tanto, los abstract modificadores y sealed no se permiten en una declaración de estructura.

Puesto que no se admite la herencia para estructuras, la accesibilidad declarada de un miembro de estructura no puede ser protected, private protectedo protected internal.

Los miembros de función de una estructura no pueden ser abstractos o virtuales, y el override modificador solo puede invalidar los métodos heredados de System.ValueType.

16.4.4 Asignación

La asignación a una variable de un tipo de estructura crea una copia del valor que se asigna. Esto difiere de la asignación a una variable de un tipo de clase, que copia la referencia, pero no el objeto identificado por la referencia.

De forma similar a una asignación, cuando se pasa un struct como parámetro de valor o se devuelve como resultado de un miembro de función, se crea una copia de la estructura. Una estructura se puede pasar por referencia a un miembro de función mediante un parámetro by-reference.

Cuando una propiedad o indexador de un struct es el destino de una asignación, la expresión de instancia asociada a la propiedad o el acceso al indexador se clasificará como una variable. Si la expresión de instancia se clasifica como un valor, se produce un error en tiempo de compilación. Esto se describe con más detalle en §12.21.2.

16.4.5 Valores predeterminados

Como se describe en §9.3, varios tipos de variables se inicializan automáticamente en su valor predeterminado cuando se crean. Para variables de tipos de clase y otros tipos de referencia, este valor predeterminado es null. Sin embargo, dado que las estructuras son tipos de valor que no pueden ser null, el valor predeterminado de un struct es el valor generado estableciendo todos los campos de tipo de valor en su valor predeterminado y todos los campos de tipo de referencia en null.

Ejemplo: Hacer referencia a la Point estructura declarada anteriormente, el ejemplo

Point[] a = new Point[100];

inicializa cada Point una de las matrices en el valor generado estableciendo los x campos y y en cero.

ejemplo final

El valor predeterminado de una estructura corresponde al valor devuelto por el constructor predeterminado de la estructura (§8.3.3). A diferencia de una clase, no se permite que un struct declare un constructor de instancia sin parámetros. En su lugar, cada estructura tiene implícitamente un constructor de instancia sin parámetros, que siempre devuelve el valor resultante de establecer todos los campos en sus valores predeterminados.

Nota: Las estructuras deben diseñarse para considerar el estado de inicialización predeterminado un estado válido. En el ejemplo

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

El constructor de instancia definido por el usuario protege contra null los valores solo en los que se llama explícitamente. En los casos en los que una KeyValuePair variable está sujeta a la inicialización del valor predeterminado, los key campos y value serán nully la estructura debe estar preparada para controlar este estado.

nota final

16.4.6 Boxing y unboxing

Un valor de un tipo de clase se puede convertir al tipo object o a un tipo de interfaz implementado por la clase simplemente tratando la referencia como otro tipo en tiempo de compilación. Del mismo modo, un valor de tipo object o un valor de un tipo de interfaz se puede convertir de nuevo a un tipo de clase sin cambiar la referencia (pero, por supuesto, se requiere una comprobación de tipo en tiempo de ejecución en este caso).

Dado que los structs no son tipos de referencia, estas operaciones se implementan de forma diferente para los tipos de estructura. Cuando se convierte un valor de un tipo de estructura en determinados tipos de referencia (como se define en §10.2.9), se realiza una operación de conversión boxing. Del mismo modo, cuando se convierte un valor de determinados tipos de referencia (como se define en §10.3.7) a un tipo de estructura, se realiza una operación de unboxing. Una diferencia clave de las mismas operaciones en los tipos de clase es que boxing y unboxing copia el valor de estructura en o fuera de la instancia boxed.

Nota: Por lo tanto, después de una operación de conversión boxing o unboxingstruct, los cambios realizados en la caja no se reflejan en el cuadro .struct nota final

Para obtener más información sobre la conversión boxing y unboxing, consulte §10.2.9 y §10.3.7.

16.4.7 Significado de esto

El significado de this en un struct difiere del significado de this en una clase, como se describe en §12.8.14. Cuando un tipo de estructura invalida un método virtual heredado de System.ValueType (como Equals, GetHashCodeo ToString), la invocación del método virtual a través de una instancia del tipo de estructura no hace que se produzca la conversión boxing. Esto es cierto incluso cuando la estructura se usa como parámetro de tipo y la invocación se produce a través de una instancia del tipo de parámetro de tipo.

Ejemplo:

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 salida del programa es:

1
2
3

Aunque es un estilo incorrecto para ToString tener efectos secundarios, en el ejemplo se muestra que no se produjo ninguna conversión boxing para las tres invocaciones de x.ToString().

ejemplo final

De forma similar, la conversión boxing nunca se produce implícitamente al acceder a un miembro en un parámetro de tipo restringido cuando el miembro se implementa dentro del tipo de valor. Por ejemplo, supongamos que una interfaz ICounter contiene un método Increment, que se puede usar para modificar un valor. Si ICounter se usa como restricción, se llama a la implementación del Increment método con una referencia a la variable en la que Increment se llamó, nunca una copia boxing.

Ejemplo:

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 primera llamada a Increment modifica el valor de la variable x. Esto no equivale a la segunda llamada a Increment, que modifica el valor en una copia boxed de x. Por lo tanto, la salida del programa es:

0
1
1

ejemplo final

Inicializadores de campo 16.4.8

Como se describe en §16.4.5, el valor predeterminado de un struct consta del valor resultante de establecer todos los campos de tipo de valor en su valor predeterminado y todos los campos de tipo de referencia en null. Por este motivo, una estructura no permite que las declaraciones de campo de instancia incluyan inicializadores de variables. Esta restricción solo se aplica a los campos de instancia. Los campos estáticos de una estructura pueden incluir inicializadores de variables.

Ejemplo: lo siguiente

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

se produce un error porque las declaraciones de campo de instancia incluyen inicializadores de variables.

ejemplo final

16.4.9 Constructores

A diferencia de una clase, no se permite que un struct declare un constructor de instancia sin parámetros. En su lugar, cada estructura tiene implícitamente un constructor de instancia sin parámetros, que siempre devuelve el valor resultante de establecer todos los campos de tipo de valor en su valor predeterminado y todos los campos de tipo de referencia en null (§8.3.3). Un struct puede declarar constructores de instancia que tienen parámetros.

Ejemplo: dado lo siguiente

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

las instrucciones crean un Point con x e y inicializado en cero.

ejemplo final

No se permite que un constructor de instancia de estructura incluya un inicializador de constructor del formulario base(argument_list), donde argument_list es opcional.

El this parámetro de un constructor de instancia de estructura corresponde a un parámetro de salida del tipo de estructura. Por lo tanto, this se asignará definitivamente (§9.4) en cada ubicación donde vuelva el constructor. Del mismo modo, no se puede leer (incluso implícitamente) en el cuerpo del constructor antes de asignarse definitivamente.

Si el constructor de instancia de estructura especifica un inicializador de constructor, ese inicializador se considera una asignación definitiva a esto que se produce antes del cuerpo del constructor. Por lo tanto, el propio cuerpo no tiene requisitos de inicialización.

Ejemplo: considere la implementación del constructor de instancia siguiente:

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

No se puede llamar a ningún miembro de función de instancia (incluidos los descriptores de acceso set para las propiedades X y Y) hasta que se hayan asignado definitivamente todos los campos de la estructura que se construye. Sin embargo, tenga en cuenta que, si Point fuera una clase en lugar de una estructura, se permitiría la implementación del constructor de instancia. Hay una excepción a esto y eso implica las propiedades implementadas automáticamente (§15.7.4). Las reglas de asignación definitivas (§12.21.2) excluyen específicamente la asignación a una propiedad automática de un tipo de estructura dentro de un constructor de instancia de ese tipo de estructura: dicha asignación se considera una asignación definitiva del campo de respaldo oculto de la propiedad automática. Por lo tanto, se permite lo siguiente:

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

ejemplo final]

16.4.10 Constructores estáticos

Los constructores estáticos para estructuras siguen la mayoría de las mismas reglas que para las clases. La ejecución de un constructor estático para un tipo de estructura se desencadena mediante el primero de los siguientes eventos que se producirán dentro de un dominio de aplicación:

  • Se hace referencia a un miembro estático del tipo de estructura.
  • Se llama a un constructor declarado explícitamente del tipo de estructura.

Nota: La creación de valores predeterminados (§16.4.5) de tipos de estructura no desencadena el constructor estático. (Un ejemplo de esto es el valor inicial de los elementos de una matriz). nota final

16.4.11 Propiedades implementadas automáticamente

Las propiedades implementadas automáticamente (§15.7.4) usan campos de respaldo ocultos, que solo son accesibles para los descriptores de acceso de propiedad.

Nota: Esta restricción de acceso significa que los constructores de estructuras que contienen propiedades implementadas automáticamente a menudo necesitan un inicializador de constructor explícito donde, de lo contrario, no necesitarían uno, para satisfacer el requisito de que se asignen definitivamente todos los campos antes de que se invoque a cualquier miembro de función o el constructor devuelva. nota final

16.4.12 Restricción de contexto seguro

16.4.12.1 General

En tiempo de compilación, cada expresión está asociada a un contexto en el que se puede acceder a esa instancia y a todos sus campos de forma segura, su contexto seguro. El contexto seguro es un contexto, que incluye una expresión, a la que es seguro que el valor se escape.

Cualquier expresión cuyo tipo de tiempo de compilación no sea una estructura ref tiene un contexto seguro del contexto del autor de la llamada.

Una default expresión, para cualquier tipo, tiene un contexto seguro del contexto de llamada.

Para cualquier expresión no predeterminada cuyo tipo de tiempo de compilación sea un struct ref tiene un contexto seguro definido por las secciones siguientes.

Los registros de contexto seguro en los que se puede copiar un valor. Dada una asignación de una expresión E1 con un contexto S1seguro , a una expresión E2 con contexto S2seguro , se produce un error si S2 es un contexto más amplio que S1.

Hay tres valores de contexto seguro diferentes, los mismos que los valores ref-safe-context definidos para las variables de referencia (§9.7.2): declaration-block, function-member y caller-context. El contexto seguro de una expresión restringe su uso de la siguiente manera:

  • Para una instrucción return e1return , el contexto seguro de debe ser contexto del autor de e1 la llamada.
  • Para una asignación e1 = e2 , el contexto seguro de e2 debe ser al menos tan amplio como contexto seguro de e1.

Para una invocación de método si hay un ref argumento o out de un ref struct tipo (incluido el receptor a menos que el tipo sea readonly), con contexto S1seguro , no es posible que ningún argumento (incluido el receptor) tenga un contexto seguro más estrecho que S1.

16.4.12.2 Contexto seguro de parámetros

Un parámetro de un tipo de estructura ref, incluido el this parámetro de un método de instancia, tiene un contexto seguro del contexto del autor de la llamada.

16.4.12.3 Contexto seguro de variables locales

Una variable local de un tipo de estructura ref tiene un contexto seguro como se indica a continuación:

  • Si la variable es una variable de iteración de un foreach bucle, el contexto seguro de la variable es el mismo que el contexto seguro de la foreach expresión del bucle.
  • De lo contrario, si la declaración de la variable tiene un inicializador, el contexto seguro de la variable es el mismo que el contexto seguro de ese inicializador.
  • De lo contrario, la variable no se inicializa en el punto de declaración y tiene un contexto seguro del contexto de llamada.

16.4.12.4 Contexto seguro del campo

Una referencia a un campo e.F, donde el tipo de F es un tipo de estructura ref, tiene un contexto seguro que es el mismo que el contexto seguro de e.

16.4.12.5 Operadores

La aplicación de un operador definido por el usuario se trata como una invocación de método (§16.4.12.6).

Para un operador que produce un valor, como e1 + e2 o c ? e1 : e2, el contexto seguro del resultado es el contexto más estrecho entre los contextos seguros de los operandos del operador. Como consecuencia, para un operador unario que produce un valor, como +e, el contexto seguro del resultado es el contexto seguro del operando.

Nota: El primer operando de un operador condicional es , boolpor lo que su contexto seguro es el contexto del autor de la llamada. Sigue que el contexto seguro resultante es el contexto seguro más estrecho del segundo y tercer operando. nota final

16.4.12.6 Invocación de método y propiedad

Un valor resultante de una invocación e1.M(e2, ...) de método o invocación e.P de propiedad tiene un contexto seguro del menor de los siguientes contextos:

  • contexto del autor de la llamada.
  • Contexto seguro de todas las expresiones de argumento (incluido el receptor).

Una invocación de propiedad (ya sea get o set) se trata como una invocación de método del método subyacente mediante las reglas anteriores.

16.4.12.7 stackalloc

El resultado de una expresión stackalloc tiene un contexto seguro de miembro de función.

16.4.12.8 Invocaciones del constructor

Una new expresión que invoca a un constructor cumple las mismas reglas que una invocación de método que se considera que devuelve el tipo que se va a construir.

Además, el contexto seguro es el más pequeño de los contextos seguros de todos los argumentos y operandos de todas las expresiones de inicializador de objeto, recursivamente, si hay algún inicializador presente.

Nota: Estas reglas se basan en Span<T> no tener un constructor de la forma siguiente:

public Span<T>(ref T p)

Este constructor hace que las instancias de Span<T> se usen como campos indistinguibles de un ref campo. Las reglas de seguridad descritas en este documento dependen de ref campos que no sean una construcción válida en C# o .NET. nota final