Compartilhar via


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ão ref 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 um async método. A partir do C# 13, uma variável ref struct não pode ser usada no mesmo bloco que a expressão await em um método async. No entanto, você pode usar variáveis ref 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, tipos ref struct e locais ref podem ser usados ​​em iteradores, desde que não estejam em segmentos de código com a instrução yield return.
  • Antes do C# 13, um ref struct não pode implementar interfaces. A partir do C# 13, um struct ref pode implementar interfaces, mas deve seguir as regras de segurança de ref. Por exemplo, um tipo ref 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, um ref struct pode ser o argumento de tipo quando o parâmetro de tipo especifica o allows ref struct em sua cláusula where.

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 um init 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 acessador init. 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 tipo ref 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. Um ref 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 tipo allows ref struct é restrito a certos tipos.
  • Um ref struct que implementa uma interface deve implementar todos os membros de instância da interface. O ref 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 structe 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.

Confira também