Classes, estruturas e registros são conceitos fundamentais na programação C#. Cada um é um tipo diferente, com diferentes recursos, capacidades e limitações. Para tornar as coisas mais confusas, eles têm características e características em comum.
Classes são tipos de referência que fornecem suporte para conceitos úteis de orientação a objetos, como encapsulamento, herança e polimorfismo. Estruturas são tipos de valor que oferecem melhor desempenho, mas possuem limitações em termos de tamanho e mutabilidade. Os registros, que foram introduzidos no C# 9, combinam o melhor de classes e estruturas, com suporte à imutabilidade por padrão.
Quando você deve usar estruturas ou registros em vez de classes em seu aplicativo? Neste artigo examinaremos as diferenças entre classes, estruturas e tipos de registro e como devemos trabalhar com esses diferentes tipos em C#.
Crie um projeto de aplicativo de console no Visual Studio
Primeiro, vamos criar um projeto de aplicativo de console .NET Core no Visual Studio. Supondo que o Visual Studio 2022 esteja instalado em seu sistema, siga as etapas descritas abaixo para criar um novo projeto de aplicativo de console .NET Core.
- Inicie o IDE do Visual Studio.
- Clique em “Criar novo projeto”.
- Na janela “Criar novo projeto”, selecione “Aplicativo de console (.NET Core)” na lista de modelos exibida.
- Clique em Avançar.
- Na janela “Configure seu novo projeto”, especifique o nome e o local do novo projeto.
- Clique em Avançar.
- Na janela “Informações adicionais” mostrada a seguir, escolha “.NET 8.0 (Long Term Support)” como a versão do framework. Deixe as caixas de seleção “Não usar instruções de nível superior” e “Ativar publicação AOT nativa” desmarcadas. Não usaremos esses recursos aqui.
- Clique em Criar.
Usaremos este projeto de aplicativo de console .NET 8 para trabalhar com exemplos de classes, estruturas e registros nas seções subsequentes deste artigo.
Usando classes em C#
Uma classe em C# é um tipo de referência. Em outras palavras, uma variável de um tipo de classe contém uma referência a um objeto. Observe que você pode ter múltiplas referências que apontam para o mesmo objeto (portanto, modificar o objeto por meio de uma referência alterará seu valor para outras). Os membros de uma classe (isto é, seus campos, propriedades, métodos, eventos, etc.) definem o comportamento e o estado das instâncias da classe.
Classes em C# suportam abstração, encapsulamento, herança e polimorfismo. Estes são os quatro princípios básicos da programação orientada a objetos.
O trecho de código a seguir mostra a sintaxe para definir uma classe em C#.
<modifiers> class <name of the class> { //Data members and member functions }
O trecho de código a seguir ilustra uma classe C# típica.
public class Author { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public string Email { get; set; } public string Phone { get; set; } }
O trecho de código abaixo mostra como você pode instanciar a classe Author e definir valores para cada uma de suas propriedades.
var author = new Author() { Id = 1, FirstName = "Joydip", LastName = "Kanjilal", Address = "Hyderabad, Imdia", Email = "[email protected]", Phone = "1234567890" };
Usando estruturas em C#
Uma estrutura em C# é um tipo de valor. Uma variável de um tipo struct contém uma instância do tipo (não uma referência). Você pode usar estruturas em C# para criar pequenos tipos de dados compostos, evitando a sobrecarga da coleta de lixo. As estruturas também podem se tornar imutáveis, usando o modificador readonly.
Observe que as instâncias de structs são passadas por valor quando você as usa como parâmetro de método. Da mesma forma, quando você atribui uma variável struct a outra variável struct, seu valor é copiado.
Embora você possa usar campos, propriedades e métodos em uma estrutura, não é possível implementar conceitos orientados a objetos, como abstração, encapsulamento, herança e polimorfismo, usando uma estrutura.
O trecho de código a seguir mostra a sintaxe para definir uma estrutura em C#.
<modifiers> struct <name of the struct> { //Data members and member functions }
O trecho de código a seguir ilustra uma estrutura típica.
struct Coordinate { public int x; public int y; }
Agora você pode usar o seguinte trecho de código para criar uma instância dessa estrutura e inicializar seus membros de dados.
Coordinate point = new Coordinate(); point.x = 10; point.y = 20;
Usando registros em C#
Os tipos de registro foram introduzidos no C# 9 para representar valores imutáveis. Os registros são tipos de referência como classes, mas possuem semântica de igualdade baseada em valor por padrão, como estruturas. Você pode usar tipos de registro em C# para criar tipos imutáveis e objetos thread-safe.
Os registros fornecem recursos integrados úteis, como uma expressão with para criar um novo registro com dados modificados e um acessador init para definir valores para suas propriedades apenas no momento da inicialização. Os tipos de registro podem ter propriedades, campos, métodos, eventos e construtores e fornecem suporte limitado para herança. No entanto, eles não suportam abstração, encapsulamento e polimorfismo.
Você pode aproveitar os tipos de registro para representar objetos de transferência de dados (DTOs), bem como outras estruturas de dados que exigem imutabilidade e semântica de igualdade.
Considere o código a seguir que mostra uma classe C# chamada Rectangle.
public class Rectangle { public int Length { get; set; } public int Breadth { get; set; } }
Você pode representar os mesmos dados usando um tipo de registro de uma forma muito mais simples:
public record Rectangle(int Length, int Breadth);
Agora você pode criar uma instância do tipo de registro Rectangle usando o código a seguir.
var rectangle = new Rectangle(10, 5);
Usando herança em tipos de registro em C#
Um tipo de registro em C# pode herdar de outro tipo de registro, mas não pode herdar de uma classe. O trecho de código a seguir mostra como um tipo de registro pode estender outro tipo de registro em C#.
public record Person(string FirstName, string LastName, string Address) { } public record Author(int id, string LastBookAuthored, string FirstName, string LastName, string Address) : Person(FirstName, LastName, Address) { public int Id { get; set; } public string LastBookAuthored { get; set; } }
Agora você pode criar uma instância do tipo de registro usando o código a seguir.
var author = new Author(1, "Mastering C# 8.0", "Joydip", "Kanjilal", "Hyderabad, India");
Classes x estruturas x registros em C#
Use classes para representar lógica e comportamento complexos em seu aplicativo. As classes foram projetadas para modelar estruturas de dados complexas que requerem conceitos orientados a objetos, como abstração, encapsulamento, composição, herança e polimorfismo. No entanto, as classes apresentam certas desvantagens de desempenho que você deve ter em mente ao projetar seus aplicativos.
Os tipos de valor (estruturas) são muito mais econômicos em termos de alocação e desalocação de memória do que os tipos de referência (classes e registros). Quando você deseja criar um tipo de dados composto com apenas alguns membros de dados, uma estrutura é uma boa escolha. As estruturas são ideais para estruturas de dados pequenas (menos de 16 bytes de tamanho) que requerem semântica de valor. Usar uma estrutura nesses casos ajudará a evitar custos de coleta de lixo e despesas gerais relacionadas.
Os tipos de registro preenchem uma lacuna entre os tipos de referência e os tipos de valor e ajudam você a escrever um código limpo, enxuto e legível. Escolha um tipo de registro em vez de uma classe ou estrutura quando os dados forem sua principal preocupação. Use tipos de registro para criar objetos de transferência de dados, objetos de resposta de API, objetos de configuração, modelos imutáveis e objetos de valor em design orientado por domínio.
Os tipos de registro fornecem excelente suporte para correspondência de padrões, tornando-os uma boa opção para trabalhar com estruturas de dados complexas. Os tipos de registro são projetados para serem tipos de dados imutáveis por padrão — um recurso que facilita a programação funcional, onde você não pode modificar uma instância depois de criada.
Ao decidir se deve usar classes, estruturas ou registros em C#, considere o uso pretendido, os recursos, a comparação de igualdade, a imutabilidade e as características de desempenho. Resumindo, as classes são adequadas quando você precisa de objetos com comportamento e lógica complexa, as estruturas são adequadas para valores leves e comportamento mínimo e os registros são ideais para estruturas de dados imutáveis com regras de igualdade diretas.