Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Um tipo de estrutura (ou tipo de struct) é um tipo de valor que pode encapsular dados e funcionalidades relacionadas.
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para recursos em visualizações públicas para a próxima versão do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
Use 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})";
}
Para obter informações sobre os tipos ref struct e readonly ref struct, confira o artigo tipos de estrutura ref.
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, o sistema copia valores variáveis na atribuição, ao passar um argumento para um método e ao retornar um resultado do método. Para variáveis de tipo de estrutura, o sistema copia uma instância do tipo. Para obter mais informações, confira 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 e uma instância de tempo. Se você estiver focado no comportamento de um tipo, considere definir uma classe. Os tipos de classe têm semântica 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 à instância em si.
Como os tipos de estrutura têm semântica de valor, recomendamos que você defina tipos de estrutura imutáveis.
Struct readonly
Use o readonly modificador para declarar que um tipo de estrutura é imutável. Todos os membros de dados de um struct readonly devem ser somente leitura da seguinte maneira:
- Qualquer declaração de campo deve ter o modificador
readonly. - Qualquer propriedade, incluindo as implementadas automaticamente, deve ser somente leitura ou
initapenas. Os setters somente init estão disponíveis somente em C# versão 9 em diante.
Essa regra garante que nenhum membro de um readonly struct modifique o estado do struct. Todos os outros membros de instância, exceto construtores, estão implicitamente readonly.
Observação
Em um struct readonly, um membro de dados de um tipo de referência mutável ainda pode alterar seu próprio estado. Por exemplo, você não pode substituir uma instância List<T>, mas pode adicionar novos elementos a ela.
O seguinte código define um struct readonly com setters de propriedade somente inicialização:
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})";
}
Membros da instância readonly
Use o readonly modificador para declarar que um membro de instância não modifica o estado de um struct. Se você não puder declarar todo o tipo de estrutura como readonly, use o modificador readonly para marcar os membros da instância que não modificam o estado do struct.
Dentro de um readonly membro de instância, você não pode atribuir aos campos de instância da estrutura. No entanto, um membro readonly pode chamar um não membro readonly. Nesse caso, o compilador cria uma cópia da instância da estrutura e chama o membro não readonly nessa cópia. Como resultado, a instância de estrutura original não é modificada.
Normalmente, você aplica o modificador readonly aos seguintes tipos de membros de instância:
Métodos:
public readonly double Sum() { return X + Y; }Você também pode aplicar o modificador
readonlyaos métodos que substituem os 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
readonlya ambos os acessadores de uma propriedade ou indexador, aplique-o na declaração da propriedade ou do indexador.Observação
O compilador declara um
getacessador de uma propriedade implementada automaticamente comoreadonly, independentemente da presença doreadonlymodificador em uma declaração de propriedade.Você pode aplicar o modificador
readonlya uma propriedade ou a um indexador com um acessadorinit:public readonly double X { get; init; }
Você pode aplicar o readonly modificador a campos estáticos de um tipo de estrutura, mas não a outros membros estáticos, como propriedades ou métodos.
O compilador pode fazer uso do readonly modificador para otimizações de desempenho. Para obter mais informações, confira Como evitar alocações.
Mutação não destrutiva
Use a with expressão para criar uma cópia de uma instância do tipo estrutura com as propriedades e os campos especificados alterados. Use a sintaxe do inicializador de objeto para especificar quais membros modificar e seus novos valores, como mostra o exemplo a seguir:
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})";
}
public static void Main()
{
var p1 = new Coords(0, 0);
Console.WriteLine(p1); // output: (0, 0)
var p2 = p1 with { X = 3 };
Console.WriteLine(p2); // output: (3, 0)
var p3 = p1 with { X = 1, Y = 4 };
Console.WriteLine(p3); // output: (1, 4)
}
Struct record
Você pode definir tipos de estrutura de registro. Os tipos de registro fornecem funcionalidade interna para encapsular dados. Você pode definir os tipos record struct e readonly record struct. Um struct de registro não pode ser um ref struct. Saiba mais e obtenha exemplos em Registros.
Matrizes embutidas
A partir do C# 12, você pode declarar matrizes embutidas como um struct tipo:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
private char _firstElement;
}
Uma matriz embutida é uma estrutura que contém um bloco contíguo de N elementos do mesmo tipo. É um equivalente de código seguro da declaração buffer fixo disponível apenas em código inseguro. Uma matriz embutida é um struct com as seguintes características:
- Ele contém um único campo.
- O struct não especifica um layout explícito.
Além disso, o compilador valida o atributo System.Runtime.CompilerServices.InlineArrayAttribute:
- O comprimento deve ser maior que zero (
> 0). - O tipo de destino deve ser um struct.
Na maioria dos casos, você pode acessar uma matriz embutida como uma matriz, tanto para ler quanto gravar valores. Você também pode usar os operadores de intervalo e índice .
Há restrições mínimas no tipo de campo único de uma matriz embutida. Não pode ser um tipo de ponteiro:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
private unsafe char* _pointerElement; // CS9184
}
Mas pode ser qualquer tipo de referência ou qualquer tipo de valor:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
private string _referenceElement;
}
Você pode usar matrizes embutidas com quase qualquer estrutura de dados C#.
As matrizes embutida são um recurso avançado da linguagem. Destinam-se a cenários de alto desempenho em que um bloco de elementos embutidos e contíguo é mais rápido do que outras estruturas de dados alternativas. Você pode saber mais sobre matrizes embutidas com base na especificação de recursos.
Inicialização de struct e valores padrão
Uma variável de um tipo structcontém diretamente os dados para struct. Esse armazenamento de dados direto cria uma distinção entre um valor não inicializado struct, que tem seu valor padrão e um inicializado struct, que armazena valores definidos pela construção dele. Por exemplo, considere o seguinte código:
public readonly struct Measurement
{
public Measurement()
{
Value = double.NaN;
Description = "Undefined";
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public double Value { get; init; }
public string Description { get; init; }
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement();
Console.WriteLine(m1); // output: NaN (Undefined)
var m2 = default(Measurement);
Console.WriteLine(m2); // output: 0 ()
var ms = new Measurement[2];
Console.WriteLine(string.Join(", ", ms)); // output: 0 (), 0 ()
}
Como mostra o exemplo anterior, a expressão de valor padrão ignora um construtor sem parâmetros e produz o valor padrão do tipo de estrutura. A instanciação de matriz do tipo estrutura também ignora um construtor sem parâmetros e produz uma matriz preenchida com os valores padrão de um tipo de estrutura.
A situação mais comum em que você verá valores padrão está em matrizes ou em outras coleções em que o armazenamento interno inclui blocos de variáveis. O exemplo a seguir cria uma matriz de 30 estruturas TemperatureRange, cada uma com o valor padrão:
// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];
Todos os campos membros de um struct precisam ser atribuídos definitivamente quando ele é criado porque os tipos struct armazenam dados diretamente. O default valor de um struct definitivamente atribui todos os campos a 0. Todos os campos devem ser atribuídos definitivamente quando um construtor é invocado. Você inicializa campos usando os seguintes mecanismos:
- Adicione inicializadores de campo a qualquer campo ou propriedade implementada automaticamente.
- Inicialize quaisquer campos ou propriedades automáticas no corpo do construtor.
Se você não inicializar todos os campos em um struct, o compilador adicionará código ao construtor que inicializa esses campos ao valor padrão. Um struct atribuído ao seu valor de default é inicializado para o padrão de 0 bit. Um struct inicializado com new é inicializado para o padrão de 0 bit, seguido pela execução de qualquer inicializador de campo e um construtor.
public readonly struct Measurement
{
public Measurement(double value)
{
Value = value;
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public Measurement(string description)
{
Description = description;
}
public double Value { get; init; }
public string Description { get; init; } = "Ordinary measurement";
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement(5);
Console.WriteLine(m1); // output: 5 (Ordinary measurement)
var m2 = new Measurement();
Console.WriteLine(m2); // output: 0 ()
var m3 = default(Measurement);
Console.WriteLine(m3); // output: 0 ()
}
Cada struct tem um construtor sem parâmetro public. Se você escrever um construtor sem parâmetros, ele deverá ser público. Se um struct declarar qualquer inicializador de campo, ele deverá declarar explicitamente um constructo. Esse constructo não precisa ser sem parâmetros. Se um struct declarar um inicializador de campo, mas nenhum constructo, o compilador relatará um erro. Qualquer constructo declarado explicitamente (com parâmetros ou sem parâmetros) executa todos os inicializadores de campo para esse struct. Todos os campos sem um inicializador de campo ou uma atribuição em um constructo são definidos como valor padrão. Para obter mais informações, consulte nota de proposta de recurso de construtores de struct sem parâmetros.
Começando no C# 12, tipos struct podem definir um construtor primário como parte de sua declaração. Os construtores primários fornecem uma sintaxe concisa para os parâmetros do construtor que podem ser usados em todo o corpo struct, em qualquer declaração de membro para esse struct.
Se todos os campos de instância de um tipo de estrutura estiverem acessíveis, você também poderá instanciá-lo sem o operador new. 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)
}
}
Nos tipos de valor internos, use os literais correspondentes para especificar um valor do tipo.
Limitações com o design de um tipo de estrutura
Os structs têm a maioria dos recursos de um tipo de classe. Há algumas exceções:
- Um tipo de estrutura não pode herdar de outro tipo de classe ou 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 dentro de um tipo de estrutura.
- Um construtor de um tipo de estrutura deve inicializar todos os campos de instância 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. Passar por valor 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 ref, out, in ou ref readonly método para indicar que um argumento deve ser passado por referência . Use ref returns para retornar um resultado de método por referência. Para obter mais informações, confira Evitar alocações.
Restrição struct
Use 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 à restrição struct.
Conversões
Para qualquer tipo de estrutura (exceto ref struct tipos), existem conversões de conversões de conversão e de unboxing de e System.Object para tiposSystem.ValueType. Conversões de boxing e unboxing também existem entre um tipo de estrutura e qualquer interface que ela implementa.
Especificação da linguagem C#
Para saber mais, confira a seção Structs da Especificação da linguagem C#.
Para mais informações sobre struct recursos, consulte as seguintes notas sobre a proposta de recurso:
- Structs somente leitura
- Membros da instância readonly
- Construtores struct sem parâmetros
-
Permitir expressão
withem structs - Estruturas de registro
- Structs de padrão automático