16 Estruturas
16.1 Generalidades
Structs são semelhantes a classes na medida em que representam estruturas de dados que podem conter membros de dados e membros de função. No entanto, ao contrário das classes, structs são tipos de valor e não exigem alocação de heap. Uma variável de um struct
tipo contém diretamente os dados do struct
, enquanto uma variável de um tipo de classe contém uma referência aos dados, este último conhecido como um objeto.
Nota: As estruturas são particularmente úteis para pequenas estruturas de dados que têm semântica de valor. Números complexos, pontos em um sistema de coordenadas ou pares chave-valor em um dicionário são bons exemplos de estruturas. A chave para essas estruturas de dados é que elas têm poucos membros de dados, que não exigem o uso de herança ou semântica de referência, em vez disso, podem ser convenientemente implementadas usando semântica de valor onde a atribuição copia o valor em vez da referência. Nota final
Conforme descrito no §8.3.5, os tipos simples fornecidos pelo C#, como int
, double
e bool
, são, na verdade, todos os tipos struct.
16.2 Declarações de estrutura
16.2.1 Generalidades
Um struct_declaration é um type_declaration (§14.7) que declara uma nova estrutura:
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
Um struct_declaration consiste num conjunto opcional de atributos (§22), seguido por um conjunto opcional de struct_modifiers (§16.2.2), seguido por um modificador opcional ref
(§16.2.3), seguido por um modificador parcial opcional (§15.2.7), seguido pela palavra-chave struct
e um identificador que nomeia o struct, seguido por uma especificação type_parameter_list opcional (§15.2.3), seguido de uma especificação struct_interfaces opcional (§16.2.5), seguida de uma especificação opcional de cláusulas type_parameter_constraints (§15.2.5), seguida de uma struct_body (§16.2.6), opcionalmente seguida de ponto e vírgula.
Uma declaração struct só pode fornecer um type_parameter_constraints_clauses se fornecer também um type_parameter_list.
Uma declaração struct que fornece um type_parameter_list é uma declaração struct genérica. Além disso, qualquer struct aninhado dentro de uma declaração de classe genérica ou uma declaração struct genérica é ela própria uma declaração struct genérica, uma vez que os argumentos de tipo para o tipo que contém devem ser fornecidos para criar um tipo construído (§8.4).
Uma declaração struct que inclua uma ref
palavra-chave não deve ter uma parte struct_interfaces .
16.2.2 Modificadores de estrutura
Um struct_declaration pode, opcionalmente, incluir uma sequência de struct_modifiers:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§23.2) só está disponível em código não seguro (§23).
É um erro em tempo de compilação para o mesmo modificador aparecer várias vezes em uma declaração struct.
Com exceção readonly
de , os modificadores de uma declaração struct têm o mesmo significado que os de uma declaração de classe (§15.2.2).
O readonly
modificador indica que o struct_declaration declara um tipo cujas instâncias são imutáveis.
Uma struct somente leitura tem as seguintes restrições:
- Cada um dos seus campos de instância também deve ser declarado
readonly
. - Nenhuma das suas propriedades de instância deve ter um set_accessor_declaration (§15.7.3).
- Não deve declarar quaisquer eventos semelhantes a campos (§15.8.2).
Quando uma instância de uma struct somente leitura é passada para um método, ela this
é tratada como um argumento/parâmetro de entrada, que não permite o acesso de gravação a quaisquer campos de instância (exceto por construtores).
16.2.3 Modificador de ref
O ref
modificador indica que o struct_declaration declara um tipo cujas instâncias são alocadas na pilha de execução. Esses tipos são chamados de tipos ref struct . O ref
modificador declara que as instâncias podem conter campos ref-like e não devem ser copiadas fora de seu contexto seguro (§16.4.12). As regras para determinar o contexto seguro de uma estrutura de referência são descritas no §16.4.12.
É um erro em tempo de compilação se um tipo ref struct for usado em qualquer um dos seguintes contextos:
- Como o tipo de elemento de uma matriz.
- Como o tipo declarado de um campo de uma classe ou uma estrutura que não tem o
ref
modificador. - Ser encaixotado para
System.ValueType
ouSystem.Object
. - Como um argumento de tipo.
- Como o tipo de um elemento de tupla.
- Um método assíncrono.
- Um iterador.
- Não há conversão de um
ref struct
tipo para o tipoobject
ou o tipoSystem.ValueType
. - Um
ref struct
tipo não deve ser declarado para implementar qualquer interface. - Um método de instância declarado em
object
ou emSystem.ValueType
, mas não substituído numref struct
tipo, não deve ser chamado com um recetor desseref struct
tipo. - Um método de instância de um
ref struct
tipo não deve ser capturado pela conversão de grupo de métodos para um tipo delegado. - Uma ref struct não deve ser capturada por uma expressão lambda ou por uma função local.
Nota: A
ref struct
não deve declararasync
métodos de instância nem usar umayield return
instrução ouyield break
dentro de um método de instância, porque o parâmetro implícitothis
não pode ser usado nesses contextos. Nota final
Essas restrições garantem que uma variável do tipo não se refira à memória de ref struct
pilha que não é mais válida ou a variáveis que não são mais válidas.
16.2.4 Modificador parcial
O partial
modificador indica que esse struct_declaration é uma declaração de tipo parcial. Várias declarações struct parciais com o mesmo nome dentro de um namespace ou declaração de tipo anexa combinam-se para formar uma declaração struct, seguindo as regras especificadas no §15.2.7.
16.2.5 Interfaces Struct
Uma declaração struct pode incluir uma especificação struct_interfaces , caso em que se diz que a struct implementa diretamente os tipos de interface fornecidos. Para um tipo struct construído, incluindo um tipo aninhado declarado dentro de uma declaração de tipo genérica (§15.3.9.7), cada tipo de interface implementado é obtido substituindo, para cada type_parameter na interface dada, a type_argument correspondente do tipo construído.
struct_interfaces
: ':' interface_type_list
;
O tratamento de interfaces em várias partes de uma declaração struct parcial (§15.2.7) é discutido mais pormenorizadamente no §15.2.4.3.
As implementações de interface são discutidas mais detalhadamente no §18.6.
16.2.6 Corpo estrutural
O struct_body de um struct define os membros do struct.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Membros da estrutura
Os membros de uma estrutura consistem nos membros introduzidos por seus struct_member_declarations e os membros herdados do 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) só está disponível em código não seguro (§23).
Nota: Todos os tipos de class_member_declarations, exceto finalizer_declaration também são struct_member_declarations. Nota final
Com exceção das diferenças observadas no §16.4, as descrições dos membros da classe fornecidas nos §15.3 a §15.12 também se aplicam aos membros struct.
16.4 Diferenças de classe e estrutura
16.4.1 Generalidades
As estruturas diferem das classes de várias maneiras importantes:
- Structs são tipos de valor (§16.4.2).
- Todos os tipos struct herdam implicitamente da classe
System.ValueType
(§16.4.3). - A atribuição a uma variável de um tipo struct cria uma cópia do valor que está sendo atribuído (§16.4.4).
- O valor padrão de uma struct é o valor produzido pela definição de todos os campos como seu valor padrão (§16.4.5).
- As operações de boxe e unboxing são usadas para converter entre um tipo struct e certos tipos de referência (§16.4.6).
- O significado de é diferente dentro dos membros da
this
estrutura (§16.4.7). - As declarações de campo de instância para uma struct não podem incluir inicializadores variáveis (§16.4.8).
- Um struct não tem permissão para declarar um construtor de instância sem parâmetros (§16.4.9).
- Um struct não tem permissão para declarar um finalizador.
16.4.2 Semântica dos valores
Structs são tipos de valor (§8.3) e dizem ter semântica de valor. As classes, por outro lado, são tipos de referência (§8.2) e dizem ter semântica de referência.
Uma variável de um tipo struct contém diretamente os dados do struct, enquanto uma variável de um tipo de classe contém uma referência a um objeto que contém os dados. Quando um struct B
contém um campo de instância do tipo A
e A
é um tipo struct, é um erro em tempo de compilação para A
depender B
ou um tipo construído a partir de B
. A struct X
depende diretamente de um struct Y
se X
contiver um campo de instância do tipo Y
. Dada esta definição, o conjunto completo de structs do qual uma struct depende é o fechamento transitivo do diretamente depende da relação.
Exemplo:
struct Node { int data; Node next; // error, Node directly depends on itself }
é um erro porque
Node
contém um campo de instância de seu próprio tipo. Outro exemplostruct A { B b; } struct B { C c; } struct C { A a; }
é um erro porque cada um dos tipos
A
,B
eC
dependem uns dos outros.Exemplo final
Com classes, é possível que duas variáveis façam referência ao mesmo objeto e, portanto, é possível que operações em uma variável afetem o objeto referenciado pela outra variável. Com structs, cada uma das variáveis tem sua própria cópia dos dados (exceto no caso de parâmetros por referência), e não é possível que as operações em uma afetem a outra. Além disso, exceto quando explicitamente anuláveis (§8.3.12), não é possível que valores de um tipo struct sejam null
.
Nota: Se um struct contém um campo de tipo de referência, então o conteúdo do objeto referenciado pode ser alterado por outras operações. No entanto, o valor do campo em si, ou seja, a que objeto ele se refere, não pode ser alterado através de uma mutação de um valor struct diferente. Nota final
Exemplo: Dado o seguinte
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); } }
A saída é
10
. A atribuição dea
parab
cria uma cópia do valor eb
, portanto, não é afetada pela atribuição aa.x
. Em vez disso, tivessePoint
sido declarada como uma classe, a saída seria100
porquea
eb
faria referência ao mesmo objeto.Exemplo final
16.4.3 Herança
Todos os tipos struct herdam implicitamente da classe System.ValueType
, que, por sua vez, herda da classe object
. Uma declaração struct pode especificar uma lista de interfaces implementadas, mas não é possível para uma declaração struct especificar uma classe base.
Os tipos de estrutura nunca são abstratos e são sempre implicitamente selados. Os abstract
modificadores e sealed
não são, portanto, permitidos em uma declaração struct.
Como a herança não é suportada para structs, a acessibilidade declarada de um membro struct não pode ser protected
, private protected
ou protected internal
.
Os membros da função em uma struct não podem ser abstratos ou virtuais, e o override
modificador só pode substituir métodos herdados de System.ValueType
.
16.4.4 Cessão
A atribuição a uma variável de um tipo struct cria uma cópia do valor que está sendo atribuído. Isso difere da atribuição a uma variável de um tipo de classe, que copia a referência, mas não o objeto identificado pela referência.
Semelhante a uma atribuição, quando uma struct é passada como um parâmetro value ou retornada como resultado de um membro da função, uma cópia da struct é criada. Um struct pode ser passado por referência a um membro da função usando um parâmetro by-reference.
Quando uma propriedade ou indexador de uma struct é o destino de uma atribuição, a expressão de instância associada à propriedade ou ao acesso do indexador deve ser classificada como uma variável. Se a expressão de instância for classificada como um valor, ocorrerá um erro em tempo de compilação. Isto é descrito em mais pormenor no ponto 12.21.2.
16.4.5 Valores padrão
Conforme descrito no §9.3, vários tipos de variáveis são automaticamente inicializados para o seu valor padrão quando são criadas. Para variáveis de tipos de classe e outros tipos de referência, esse valor padrão é null
. No entanto, como structs são tipos de valor que não podem ser null
, o valor padrão de um struct é o valor produzido definindo todos os campos de tipo de valor como seu valor padrão e todos os campos de tipo de referência como null
.
Exemplo: Referindo-se ao struct declarado
Point
acima, o exemploPoint[] a = new Point[100];
Inicializa cada
Point
um na matriz para o valor produzido definindo osx
campos ey
como zero.Exemplo final
O valor padrão de um struct corresponde ao valor retornado pelo construtor padrão do struct (§8.3.3). Ao contrário de uma classe, uma struct não tem permissão para declarar um construtor de instância sem parâmetros. Em vez disso, cada struct implicitamente tem um construtor de instância sem parâmetros, que sempre retorna o valor resultante da definição de todos os campos para seus valores padrão.
Nota: Structs deve ser projetado para considerar o estado de inicialização padrão um estado válido. No exemplo
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; } }
O construtor de instância definido pelo usuário protege contra
null
valores somente quando é explicitamente chamado. Nos casos em que umaKeyValuePair
variável está sujeita à inicialização do valor padrão, oskey
campos evalue
serãonull
, e o struct deve estar preparado para lidar com esse estado.Nota final
16.4.6 Boxe e unboxing
Um valor de um tipo de classe pode ser convertido em tipo object
ou em um tipo de interface que é implementado pela classe simplesmente tratando a referência como outro tipo em tempo de compilação. Da mesma forma, um valor de tipo object
ou um valor de um tipo de interface pode ser convertido de volta para um tipo de classe sem alterar a referência (mas, é claro, uma verificação de tipo em tempo de execução é necessária neste caso).
Como structs não são tipos de referência, essas operações são implementadas de forma diferente para tipos struct. Quando um valor de um tipo struct é convertido em determinados tipos de referência (conforme definido no §10.2.9), ocorre uma operação de boxe. Da mesma forma, quando um valor de certos tipos de referência (conforme definido no §10.3.7) é convertido de volta para um tipo struct, ocorre uma operação de unboxing. Uma diferença importante das mesmas operações em tipos de classe é que o boxing e o unboxing copiam o valor struct para dentro ou para fora da instância in a box.
Nota: Assim, após uma operação de boxe ou unboxing, as alterações feitas no unboxed
struct
não são refletidas no boxedstruct
. Nota final
Para mais detalhes sobre boxe e unboxing, ver §10.2.9 e §10.3.7.
16.4.7 Significado deste
O significado de this
em uma estrutura difere do significado de this
em uma classe, conforme descrito no §12.8.14. Quando um tipo struct substitui um método virtual herdado de System.ValueType
(como Equals
, GetHashCode
, ou ToString
), a invocação do método virtual por meio de uma instância do tipo struct não faz com que ocorra boxe. Isso é verdadeiro mesmo quando o struct é usado como um parâmetro type e a invocação ocorre por meio de uma instância do type parameter type.
Exemplo:
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>(); }
A saída do programa é:
1 2 3
Embora seja mau estilo para
ToString
ter efeitos colaterais, o exemplo demonstra que nenhum boxe ocorreu para as três invocações dex.ToString()
.Exemplo final
Da mesma forma, o boxe nunca ocorre implicitamente ao acessar um membro em um parâmetro de tipo restrito quando o membro é implementado dentro do tipo de valor. Por exemplo, suponha que uma interface ICounter
contenha um método Increment
, que pode ser usado para modificar um valor. Se ICounter
for usado como uma restrição, a Increment
implementação do método é chamada com uma referência à variável que Increment
foi chamada, nunca uma cópia em caixa.
Exemplo:
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>(); }
A primeira chamada para
Increment
modificar o valor na variávelx
. Isso não é equivalente à segunda chamada paraIncrement
, que modifica o valor em uma cópia em caixa dox
. Assim, a saída do programa é:0 1 1
Exemplo final
16.4.8 Inicializadores de campo
Conforme descrito no §16.4.5, o valor padrão de uma struct consiste no valor que resulta da definição de todos os campos de tipo de valor como seu valor padrão e todos os campos de tipo de referência como null
. Por esse motivo, um struct não permite que declarações de campo de instância incluam inicializadores variáveis. Esta restrição aplica-se apenas aos campos de instância. Os campos estáticos de uma struct podem incluir inicializadores variáveis.
Exemplo: O seguinte
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }
está em erro porque as declarações de campo de instância incluem inicializadores variáveis.
Exemplo final
16.4.9 Construtores
Ao contrário de uma classe, uma struct não tem permissão para declarar um construtor de instância sem parâmetros. Em vez disso, cada struct implicitamente tem um construtor de instância sem parâmetros, que sempre retorna o valor resultante da definição de todos os campos de tipo de valor para seu valor padrão e todos os campos de tipo de referência para null
(§8.3.3). Um struct pode declarar construtores de instância com parâmetros.
Exemplo: Dado o seguinte
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); } }
As instruções criam um
Point
comx
ey
inicializadas em zero.Exemplo final
Um construtor de instância struct não tem permissão para incluir um inicializador de construtor do formulário base(
argument_list)
, onde argument_list é opcional.
O this
parâmetro de um construtor de instância struct corresponde a um parâmetro de saída do tipo struct. Como tal, this
deve ser definitivamente atribuído (§9.4) em cada local onde o construtor retorna. Da mesma forma, não pode ser lido (mesmo implicitamente) no corpo do construtor antes de ser definitivamente atribuído.
Se o construtor struct instance especificar um inicializador do construtor, esse inicializador será considerado uma atribuição definida para isso que ocorre antes do corpo do construtor. Portanto, o corpo em si não tem requisitos de inicialização.
Exemplo: Considere a implementação do construtor de instância abaixo:
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 } }
Nenhum membro da função de instância (incluindo os acessadores definidos para as propriedades
X
eY
) pode ser chamado até que todos os campos da estrutura que está sendo construída tenham sido definitivamente atribuídos. Observe, no entanto, que sePoint
fosse uma classe em vez de uma struct, a implementação do construtor de instância seria permitida. Há uma exceção a isso, e que envolve propriedades implementadas automaticamente (§15.7.4). As regras de atribuição definida (§12.21.2) isentam especificamente a atribuição a uma autopropriedade de um tipo struct dentro de um construtor de instância desse tipo struct: tal atribuição é considerada uma atribuição definida do campo de suporte oculto da propriedade automática. Assim, é permitido: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 } }
exemplo final]
16.4.10 Construtores estáticos
Os construtores estáticos para structs seguem a maioria das mesmas regras que para classes. A execução de um construtor estático para um tipo struct é acionada pelo primeiro dos seguintes eventos a ocorrer dentro de um domínio de aplicativo:
- Um membro estático do tipo struct é referenciado.
- Um construtor explicitamente declarado do tipo struct é chamado.
Nota: A criação de valores padrão (§16.4.5) de tipos struct não aciona o construtor estático. (Um exemplo disso é o valor inicial dos elementos em uma matriz.) Nota final
16.4.11 Propriedades implementadas automaticamente
As propriedades implementadas automaticamente (§15.7.4) usam campos de suporte ocultos, que só são acessíveis aos acessadores de propriedade.
Nota: Essa restrição de acesso significa que os construtores em structs contendo propriedades implementadas automaticamente geralmente precisam de um inicializador de construtor explícito onde de outra forma não precisariam de um, para satisfazer o requisito de todos os campos serem definitivamente atribuídos antes que qualquer membro da função seja invocado ou o construtor retorne. Nota final
16.4.12 Restrição de contexto seguro
16.4.12.1 Generalidades
Em tempo de compilação, cada expressão é associada a um contexto onde essa instância e todos os seus campos podem ser acessados com segurança, seu contexto seguro. O contexto seguro é um contexto, que encerra uma expressão, para a qual é seguro para o valor escapar.
Qualquer expressão cujo tipo de tempo de compilação não seja uma estrutura de referência tem um contexto seguro de contexto de chamador.
Uma default
expressão, para qualquer tipo, tem contexto seguro de contexto de chamador.
Para qualquer expressão não padrão cujo tipo de tempo de compilação é uma ref struct tem um contexto seguro definido pelas seções a seguir.
O contexto seguro registra para qual contexto um valor pode ser copiado. Dada uma atribuição de uma expressão E1
com um contexto S1
seguro , para uma expressão E2
com contexto S2
seguro , é um erro se S2
for um contexto mais amplo do que S1
.
Existem três valores diferentes de contexto seguro, os mesmos que os valores ref-safe-context definidos para variáveis de referência (§9.7.2): declaration-block, function-member e caller-context. O contexto seguro de uma expressão restringe o seu uso da seguinte forma:
- Para uma declaração
return e1
de retorno, o contexto seguro de deve ser o contexto doe1
chamador. - Para uma atribuição
e1 = e2
, o contexto seguro dee2
deve ser, pelo menos, um contexto tão amplo como o contexto seguro dee1
.
Para uma invocação de método, se houver um ref
ou out
argumento de um ref struct
tipo (incluindo o recetor, a menos que o tipo seja readonly
), com contexto S1
seguro, então nenhum argumento (incluindo o recetor) pode ter um contexto seguro mais restrito do que S1
.
16.4.12.2 Contexto seguro do parâmetro
Um parâmetro de um tipo ref struct, incluindo o this
parâmetro de um método de instância, tem um contexto seguro de caller-context.
16.4.12.3 Contexto seguro da variável local
Uma variável local de um tipo ref struct tem um contexto seguro da seguinte forma:
- Se a variável é uma variável de iteração de um
foreach
loop, então o contexto seguro da variável é o mesmo que o contexto seguro da expressão doforeach
loop. - Caso contrário, se a declaração da variável tiver um inicializador, o contexto seguro da variável será o mesmo que o contexto seguro desse inicializador.
- Caso contrário, a variável não será inicializada no ponto de declaração e terá um contexto seguro de contexto de chamador.
16.4.12.4 Contexto seguro no terreno
Uma referência a um campo e.F
, onde o tipo de é um tipo ref F
struct, tem um contexto seguro que é o mesmo que o contexto seguro de e
.
16.4.12.5 Operadores
A aplicação de um operador definido pelo utilizador é tratada como uma invocação de método (§16.4.12.6).
Para um operador que produz um valor, como e1 + e2
ou c ? e1 : e2
, o contexto seguro do resultado é o contexto mais estreito entre os contextos seguros dos operandos do operador. Como consequência, para um operador unário que produz um valor, como +e
, o contexto seguro do resultado é o contexto seguro do operando.
Nota: O primeiro operando de um operador condicional é um
bool
, portanto, seu contexto seguro é o contexto do chamador. Segue-se que o contexto seguro resultante é o contexto seguro mais estreito do segundo e terceiro operando. Nota final
16.4.12.6 Invocação de método e propriedade
Um valor resultante de uma invocação e1.M(e2, ...)
de método ou invocação e.P
de propriedade tem contexto seguro do menor dos seguintes contextos:
- contexto do chamador.
- O contexto seguro de todas as expressões de argumento (incluindo o recetor).
Uma invocação de propriedade (ou get
set
) é tratada como uma invocação de método do método subjacente pelas regras acima.
16.4.12.7 Stackalloc
O resultado de uma expressão stackalloc tem contexto seguro de function-member.
16.4.12.8 Invocação do construtor
Uma new
expressão que invoca um construtor obedece às mesmas regras que uma invocação de método que é considerada para retornar o tipo que está sendo construído.
Além disso, o contexto seguro é o menor dos contextos seguros de todos os argumentos e operandos de todas as expressões do inicializador de objeto, recursivamente, se algum inicializador estiver presente.
Nota: Estas regras baseiam-se em
Span<T>
não ter um construtor da seguinte forma:public Span<T>(ref T p)
Tal construtor torna instâncias de
Span<T>
usados como campos indistinguíveis de umref
campo. As regras de segurança descritas neste documento dependem deref
campos que não são uma construção válida em C# ou .NET. Nota final
ECMA C# draft specification