Tipos de estrutura ref
(referência de C#)
Você pode usar o modificador ref
na declaração de um tipo de estrutura. As instâncias de um tipo ref struct
são alocadas na pilha e não podem escapar para o heap gerenciado. Para garantir isso, o compilador limita o uso de tipos ref struct
da seguinte maneira:
- Um
ref struct
não pode ser o tipo de elemento de uma matriz. - Um
ref struct
não pode ser um tipo declarado de um campo de uma classe ou um nãoref struct
. - Um
ref struct
não pode ser demarcado para System.ValueType ou System.Object. - Uma variável
ref struct
não pode ser capturada em uma expressão lambda ou em uma função local. - Antes do C# 13,
ref struct
as variáveis não podem ser usadas em umasync
método. A partir do C# 13, uma variávelref struct
não pode ser usada no mesmo bloco que a expressãoawait
em um métodoasync
. No entanto, você pode usar variáveisref struct
em métodos síncronos, por exemplo, em métodos que retornam Task ou Task<TResult>. - Antes do C# 13, uma variável
ref struct
não pode ser usada em iteradores. A partir do C# 13, tiposref struct
e locaisref
podem ser usados em iteradores, desde que não estejam em segmentos de código com a instruçãoyield return
. - Antes do C# 13, um
ref struct
não pode implementar interfaces. A partir do C# 13, um structref
pode implementar interfaces, mas deve seguir as regras de segurança de ref. Por exemplo, um tiporef struct
não pode ser convertido no tipo de interface porque isso requer uma conversão de boxing. - Antes do C# 13, um
ref struct
não pode ser um argumento de tipo. A partir do C# 13, umref struct
pode ser o argumento de tipo quando o parâmetro de tipo especifica oallows ref struct
em sua cláusulawhere
.
Normalmente, você define um tipo de ref struct
quando precisa de um tipo que também inclua membros de dados de tipos de ref struct
:
public ref struct CustomRef
{
public bool IsValid;
public Span<int> Inputs;
public Span<int> Outputs;
}
Para declarar um ref struct
como readonly
, combine os modificadores readonly
e ref
na declaração de tipo (o modificador readonly
deve vir antes do ref
):
public readonly ref struct ConversionRequest
{
public ConversionRequest(double rate, ReadOnlySpan<double> values)
{
Rate = rate;
Values = values;
}
public double Rate { get; }
public ReadOnlySpan<double> Values { get; }
}
No .NET, exemplos de um ref struct
são System.Span<T> e System.ReadOnlySpan<T>.
Campos ref
A partir do C# 11, você pode declarar um campo ref
em um ref struct
, como mostra o exemplo a seguir:
public ref struct RefFieldExample
{
private ref int number;
public int GetNumber()
{
if (System.Runtime.CompilerServices.Unsafe.IsNullRef(ref number))
{
throw new InvalidOperationException("The number ref field is not initialized.");
}
return number;
}
}
Um campo ref
pode ter o valor null
. Use o método Unsafe.IsNullRef<T>(T) para determinar se um campo ref
é null
.
Você pode aplicar o modificador readonly
a um campo ref
das seguintes maneiras:
readonly ref
: você pode fazer reatribuição de ref desse campo com o operador= ref
somente dentro de um construtor ou uminit
acessador. Você pode atribuir um valor com o operador=
a qualquer momento permitido pelo modificador de acesso ao campo.ref readonly
: Em nenhum momento, você não pode atribuir um valor com o operador=
a esse campo. No entanto, você pode fazer reatribuição de ref de um campo com o operador= ref
.readonly ref readonly
: você só pode fazer reatribuição de ref desse campo em um construtor ou acessadorinit
. Em nenhum momento, você não pode atribuir um valor ao campo.
O compilador garante que uma referência armazenada no campo ref
não sobreviva ao valor ao qual se refere.
O recurso de campos ref
permite uma implementação segura de tipos como System.Span<T>:
public readonly ref struct Span<T>
{
internal readonly ref T _reference;
private readonly int _length;
// Omitted for brevity...
}
O tipo Span<T>
armazena uma referência por meio da qual acessa os elementos contíguos na memória. O uso de uma referência permite que uma instância Span<T>
evite copiar o armazenamento ao qual se refere.
O padrão descartável
Você pode definir um ref struct
descartável. Para fazer isso, verifique se um ref struct
se encaixa no padrão descartável. Ou seja, ele tem um método de instância Dispose
, que é acessível, sem parâmetros e tem um tipo void
de retorno. Você pode usar a instrução ou declaração using com uma instância de um ref struct
descartável.
A partir do C# 13, você também pode implementar IDisposable em tipos ref struct
. No entanto, a resolução de sobrecarga prefere o padrão descartável ao método de interface. O compilador resolve para um IDisposable.Dispose
método somente quando um método adequado Dispose
não é encontrado.
Restrições para tipos ref struct
que implementam uma interface
Essas restrições garantem que um tipo ref struct
que implementa uma interface obedeça às regras de segurança de ref necessárias.
- Não é possível converter um
ref struct
em uma instância de uma interface que ele implementa. Esta restrição inclui a conversão implícita quando você usa um tiporef struct
como argumento quando o parâmetro é um tipo de interface. A conversão resulta em uma conversão de boxing, que viola a segurança de ref. Umref struct
pode declarar métodos como declarações de interface explícitas. No entanto, esses métodos só podem ser acessados por métodos genéricos onde o parâmetro de tipoallows ref struct
é restrito a certos tipos. - Um
ref struct
que implementa uma interface deve implementar todos os membros de instância da interface. Oref struct
deve implementar membros de instância mesmo quando a interface inclui uma implementação padrão.
O compilador impõe essas restrições. Se você escrever tipos ref struct
que implementam interfaces, cada nova atualização poderá incluir novos membros de interface padrão. Até que você forneça uma implementação para quaisquer novos métodos de instância, seu aplicativo não será compilado. Você não pode fornecer uma implementação específica para um método de interface static
com uma implementação padrão.
Importante
Um ref struct
que implementa uma interface inclui o potencial para alterações posteriores de quebra de origem e de quebra binária. A interrupção ocorre se ref struct
implementa uma interface definida em outro assembly, e esse assembly fornece uma atualização que adiciona membros padrão a essa interface.
A quebra de origem ocorre quando você recompila o ref struct
: ele deve implementar o novo membro, mesmo que haja uma implementação padrão.
A quebra binária ocorrerá se você atualizar o assembly externo sem recompilar o tipo ref struct
e também o código atualizado chamar a implementação padrão do novo método. O runtime gera uma exceção quando o membro padrão é acessado.
Especificação da linguagem C#
Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:
Para obter mais informações sobre os campos ref
, confira a nota de proposta Aprimoramentos de struct de baixo nível.