C# 12 - Primary Constructors
Hoje vou apresentar um novo recurso previsto para o C# 12 chamado de primary constructors ou construtores primários. |
Esta
previsto para o C# 12 um novo recurso chamado de Construtores primários que visa
simplificar a inicialização de propriedades em classes, principalmente para
classes com muitas propriedades.
Antes de apresentar os Construtores Primários, vamos ver como os construtores funcionam atualmente no C#.
No C# um construtor é um método especial chamado quando um objeto de classe é criado sendo usados para inicializar o estado de um objeto, o que normalmente envolve definir os valores das propriedades da classe.
A seguir temos um exemplo clássico de uma classe Funcionario com um construtor :
public
class
Funcionario { public string Nome { get; set; } public string Sobrenome { get; set; } public DateTime DataContratacao { get; set; } public decimal Salario { get; set; } public Funcionario(string nome, string sobrenome, DateTime dataContratacao, decimal salario) { Nome = nome; Sobrenome = sobrenome; DataContratacao = dataContratacao; Salario = salario; } } |
O que são construtores primários?
Um construtor primário é um recurso que permite definir e inicializar
propriedades diretamente na lista de parâmetros do construtor. Esse recurso
elimina a necessidade de código repetido e torna seu código mais conciso e
legível.
E você pode se surpreender, mas já temos o recurso dos construtores primários desde o C# 9 definidos no recurso record. Considere o seguinte record:
public
record
Pessoa(string
Nome, int
Idade) { public Pessoa() : this("Maria", 31) { } } var maria = new Person("Maria", 31); |
Podemos ver que os records definem seu construtor primário diretamente na definição.
Usando construtores primários
Vamos começar com um exemplo de uso de construtores primários com uma classe
Funcionario. Vejamos a seguir como definir uma
classe Funcionario usando um construtor primário.
Para isso vamos usar o VS 2022 Preview e criar um projeto Console App usando a versão do .NET 8.0 preview :
E para poder usar
a versão do C# 12 no projeto teremos que incluir no arquivo de projeto a tag:
<LangVersion>preview</LangVersion> :
<Project
Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <LangVersion>preview</LangVersion> <Nullable>enable</Nullable> </PropertyGroup> </Project> |
var
funcionario1 =
new
Funcionario("Maria",
"Silveira",
new
DateTime(2022, 1, 1), 50000); Console.WriteLine(funcionario1.Nome); Console.ReadKey(); public class Funcionario(string nome, string sobrenome, DateTime dataContratacao, decimal salario){ public string Nome { get; init; } = nome; public string Sobrenome { get; init; } = sobrenome; public DateTime DataContratacao { get; init; } = dataContratacao; public decimal Salario { get; init; } = salario; } |
Neste exemplo,
definimos uma classe Funcionario com quatro propriedades:
Nome, Sobrenome, DataContratacao e Salario. Também usamos a palavra-chave
init para tornar as propriedades somente
leitura e as inicializamos diretamente na lista de parâmetros do construtor
primário.
A seguir usamos o código abaixo para criar um novo objeto
Funcionario usando o Construtor Primário :
var funcionario1 = new Funcionario("Maria", "Silveira", new DateTime(2022, 1, 1), 50000);
Executando o projeto iremos obter:
O
objeto Funcionario é criado com os parâmetros
nome, sobrenome, dataContratacao e salario, que são usados para inicializar
as propriedades correspondentes.
Observe que a classe Funcionario não tem um método construtor com um construtor
sem parâmetros, portanto, você obterá um erro do compilador se tentar criar um
objeto usando o construtor padrão.
Os construtores primários funcionam bem com classes que possuem muitas
propriedades, pois reduzem a quantidade de código clichê necessário para
inicializá-los. Além disso, o uso de construtores primários torna seu código
mais legível e reduz o risco de erros causados por instruções de inicialização
ausentes ou ordenadas incorretamente.
Assim, a capacidade de uma classe ou struct em C# ter mais de um
construtor fornece generalidade, mas às custas de algum tédio na sintaxe da
declaração, porque a entrada do construtor e o estado da classe precisam ser
claramente separados.
Os construtores primários colocam os parâmetros de um construtor no escopo para
que toda a classe seja usada para inicialização ou diretamente como estado do
objeto. A desvantagem é que qualquer outro construtor deve chamar por meio do
construtor primário.
Há também uma diferença entre o recurso usado nos records e o novo construtor primário.
Vamos comparar a definição de um record com uma classe que usa um construtor primário :
1- record
public
record
Pessoa(string
Nome, int
Idade) { public Pessoa() : this("Maria", 31) { } } |
2- Classe
public
class
Pessoa(string
nome, int
idade) { public string Nome { get; set; } = nome; public int Idade { get; set; } = idade; } |
A diferença é que na classe os parâmetros são escritos em letras minúsculas e no record usamos PascalCase ou seja maiúsculas e minúsculas.
A razão aqui é simples : em contraste com os records, o construtor primário atua mais como um argumento do que como uma propriedade. É por isso que você pode ver que definimos esses parâmetros para as propriedades.
Assim como os records, quando você declara um construtor primário, você DEVE usá-lo ao declarar construtores explícitos da seguinte forma:
var
produto1 =
new Produto(); var produto2 = new Produto("Produto1");var produto3 = new Produto("Meu produto", 1);Console.Readkey(); public class Produto(string nome, int categoriaId){ public Produto() : this("Default", -1) { } public Produto(string nome) : this(nome, 0) { } public string? Nome => nome; public int CategoriaId => categoriaId; public string? ProdutoId => $"{nome}-{categoriaId}"; } |
Como você pode ver, é requerido o uso da palavra-chave
this() nos demais construtores , que invocam o construtor
primário. Se você omitir o this, o compilador vai
gerar a seguinte mensagem de erro:
Erro CS8862 Um
construtor declarado em um tipo com lista de parâmetros deve ter o inicializador
de construtor 'this'.
O comportamento do Construtor Primário em structs é o mesmo, visto que já tínhamos uma sintaxe semelhante na record struct com o construtor sem parâmetros trazido pelo C# 11.
E estamos conversados.
"Sabemos que permanecemos nele, e ele em nós, porque ele nos deu do seu
Espírito.
E vimos e testemunhamos que o Pai enviou seu Filho para ser o Salvador do
mundo."
1 João 4:13,14
Referências: