Tipos de estrutura (referência C#)

Um tipo de estrutura (ou tipo de struct) é um tipo de valor que pode encapsular dados e funcionalidade relacionada. Você usa a struct palavra-chave para definir um tipo de estrutura:

public struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }

    public override string ToString() => $"({X}, {Y})";
}

Os tipos de estrutura têm semântica de valor. Ou seja, uma variável de um tipo de estrutura contém uma instância do tipo. Por padrão, os valores de variáveis são copiados na atribuição, passando um argumento para um método e retornando um resultado de método. No caso de uma variável de tipo de estrutura, uma instância do tipo é copiada. Para obter mais informações, consulte tipos de valor.

Normalmente, você usa tipos de estrutura para criar pequenos tipos centrados em dados que fornecem pouco ou nenhum comportamento. Por exemplo, o .NET usa tipos de estrutura para representar um número ( inteiro e real), um valor booliano, um caractere Unicode, uma instância de tempo. Se você estiver concentrado no comportamento de um tipo, considere definir uma classe. Os tipos de classe têm semânticas de referência. Ou seja, uma variável de um tipo de classe contém uma referência a uma instância do tipo, não a instância em si.

Como os tipos de estrutura têm semântica de valor, recomendamos que você defina tipos de estrutura imutáveis .

readonly struct

A partir do C# 7,2, você usa o readonly modificador para declarar que um tipo de estrutura é imutável. Todos os membros de dados de um readonly struct devem ser somente leitura da seguinte maneira:

  • Qualquer declaração de campo deve ter o readonly modificador
  • Qualquer propriedade, incluindo aquelas auto-implementadas, deve ser somente leitura. No C# 9.0 e posterior, uma propriedade pode ter init um acessador.

Isso garante que nenhum membro de readonly um struct modifice o estado do struct. No C# 8.0 e posterior, isso significa que outros membros da instância, exceto construtores, são implicitamente readonly .

Observação

Em um readonly struct, um membro de dados de um tipo de referência mutável ainda pode modificar seu próprio estado. Por exemplo, você não pode substituir uma List<T> instância, mas pode adicionar novos elementos a ela.

O código a seguir define um struct com setters de propriedade somente readonly init, disponíveis no C# 9.0 e posterior:

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

readonly membros da instância

A partir do C# 8.0, você também pode usar o modificador para declarar que um membro da instância não modifica o estado de readonly um struct. Se você não puder declarar o tipo de estrutura inteira como , use o modificador para marcar os membros da instância que não modificam o estado readonly readonly do struct.

Dentro de readonly um membro de instância, você não pode atribuir aos campos de instância da estrutura. No entanto, readonly um membro pode chamar um não readonly membro. Nesse caso, o compilador cria uma cópia da instância de estrutura e chama o não membro readonly nessa cópia. Como resultado, a instância da estrutura original não é modificada.

Normalmente, você aplica o readonly modificador aos seguintes tipos de membros de instância:

  • Métodos:

    public readonly double Sum()
    {
        return X + Y;
    }
    

    Você também pode aplicar o readonly modificador a métodos que substituem métodos declarados em System.Object :

    public readonly override string ToString() => $"({X}, {Y})";
    
  • propriedades e indexadores:

    private int counter;
    public int Counter
    {
        readonly get => counter;
        set => counter = value;
    }
    

    Se você precisar aplicar o modificador a ambos os acessadores de uma propriedade ou indexador, aplique-o na declaração da propriedade readonly ou do indexador.

    Observação

    O compilador declara um acessador de uma propriedade auto-implementada como , independentemente da presença do get readonly readonly modificador em uma declaração de propriedade.

    No C# 9,0 e posterior, você pode aplicar o readonly modificador a uma propriedade ou a um indexador com um init acessador:

    public readonly double X { get; init; }
    

Você não pode aplicar o readonly modificador a membros estáticos de um tipo de estrutura.

O compilador pode fazer uso do readonly modificador para otimizações de desempenho. Para obter mais informações, consulte escrever código C# seguro e eficiente.

Limitações com o design de um tipo de estrutura

Ao criar um tipo de estrutura, você tem os mesmos recursos que com um tipo de classe , com as seguintes exceções:

  • Não é possível declarar um construtor sem parâmetros. Cada tipo de estrutura já fornece um Construtor implícito sem parâmetros que produz o valor padrão do tipo.

  • Não é possível inicializar um campo ou propriedade de instância em sua declaração. No entanto, você pode inicializar um campo estático ou const ou uma propriedade estática em sua declaração.

  • Um construtor de um tipo de estrutura deve inicializar todos os campos de instância do tipo.

  • Um tipo de estrutura não pode herdar de outra classe ou tipo de estrutura e não pode ser a base de uma classe. No entanto, um tipo de estrutura pode implementar interfaces.

  • Você não pode declarar um finalizador em um tipo de estrutura.

Instanciação de um tipo de estrutura

Em C#, você deve inicializar uma variável declarada antes que ela possa ser usada. Como uma variável de tipo de estrutura não pode ser null (a menos que seja uma variável de um tipo de valor anulável), você deve instanciar uma instância do tipo correspondente. Há várias maneiras de fazer isso.

Normalmente, você cria uma instância de um tipo de estrutura chamando um construtor apropriado com o new operador. Cada tipo de estrutura tem pelo menos um construtor. Esse é um construtor sem parâmetro implícito, que produz o valor padrão do tipo. Você também pode usar uma expressão de valor padrão para produzir o valor padrão de um tipo.

Se todos os campos de instância de um tipo de estrutura são acessíveis, você também pode instancia-lo sem o new operador . Nesse caso, você deve inicializar todos os campos de instância antes do primeiro uso da instância. O seguinte exemplo mostra como fazer isso:

public static class StructWithoutNew
{
    public struct Coords
    {
        public double x;
        public double y;
    }

    public static void Main()
    {
        Coords p;
        p.x = 3;
        p.y = 4;
        Console.WriteLine($"({p.x}, {p.y})");  // output: (3, 4)
    }
}

No caso dos tipos de valor integrados, use os literais correspondentes para especificar um valor do tipo .

Passando variáveis de tipo de estrutura por referência

Quando você passa uma variável de tipo de estrutura para um método como um argumento ou retorna um valor de tipo de estrutura de um método, toda a instância de um tipo de estrutura é copiada. Isso pode afetar o desempenho do código em cenários de alto desempenho que envolvem tipos de estrutura grandes. Você pode evitar a cópia de valor passando uma variável de tipo de estrutura por referência. Use os modificadores de parâmetro de método , ou ref para indicar que um argumento deve ser passado por out in referência. Use ref returns para retornar um resultado de método por referência. Para obter mais informações, consulte Escrever código C# seguro e eficiente.

ref Struct

A partir do C# 7.2, você pode usar o ref modificador na declaração de um tipo de estrutura. Instâncias de um ref tipo de 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 de struct da seguinte forma:

  • Um ref struct não pode ser o tipo de elemento de uma matriz.
  • Um struct não pode ser um tipo declarado de um campo de uma classe ref ou um ref struct não.
  • Um ref struct não pode implementar interfaces.
  • Um ref struct não pode ser a box para System.ValueType ou System.Object .
  • Um ref struct não pode ser um argumento de tipo.
  • Uma ref variável de struct não pode ser capturada por uma expressão lambda ou uma função local.
  • Uma ref variável de struct não pode ser usada em um async método. No entanto, você pode usar ref variáveis struct em métodos síncronos, por exemplo, naquelas que retornam Task ou Task<TResult> .
  • Uma ref variável de struct não pode ser usada em iteradores.

Normalmente, você define um ref tipo de struct quando precisa de um tipo que também inclui membros de dados de ref tipos de struct:

public ref struct CustomRef
{
    public bool IsValid;
    public Span<int> Inputs;
    public Span<int> Outputs;
}

Para declarar uma ref struct como readonly , combine os readonly ref modificadores e na declaração de tipo (o readonly modificador deve vir antes do ref Modificador):

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 uma ref estrutura são System.Span<T> e System.ReadOnlySpan<T> .

restrição de struct

Você também usa a struct palavra-chave na struct restrição para especificar que um parâmetro de tipo é um tipo de valor não anulável. Os tipos de estrutura e Enumeração atendem à struct restrição.

Conversões

Para qualquer tipo de estrutura (exceto tipos de ref struct ), existem conversões boxing e unboxing de e para os System.ValueType System.Object tipos e. Existem também conversões boxing e unboxing entre um tipo de estrutura e qualquer interface que ela implementa.

Especificação da linguagem C#

Para obter mais informações, consulte a seção structs da especificação da linguagem C#.

Para obter mais informações sobre os recursos introduzidos no C# 7,2 e posterior, consulte as seguintes notas de proposta de recurso:

Confira também