C# 9.0 -  Target-typed com expressões new


Hoje veremos o novo recurso Target-type new expressions (expressões new com tipo de destino) do C# 9.0.

O novo recurso do C# 9 chamado de Target-type new expressions tem como objetivo não exigir especificação de tipo para construtores quando o tipo é conhecido.

O termo Target-type ou tipo de destino significa que uma expressão obtém o tipo a partir do contexto no qual  é usada.

Assim no C# 9.0 a expressão new obtém o tipo a partir do contexto, o que significa que você não precisa especificar o tipo de destino explicitamente para chamar um construtor.  Vejamos um exemplo:

Usando o recurso target-type new expressions

Vamos criar um projeto Console e neste projeto criar a classe Aluno onde vamos definir um construtor padrão e um construtor com parâmetros:

    public class Aluno
    {
        public Aluno() { }
        public Aluno(string nome, string email)
        {
            Nome = nome;
            Email = email;
        }
        public string Nome { get; set; }
        public string Email { get; set; }
    }

Para criar uma instância desta classe podemos usar o seguinte código:


    Aluno  aluno  = new Aluno();        

Podemos também criar um objeto usando o construtor parametrizado:


    Aluno  aluno  = new Aluno("Maria", "maria@email.com");        

Como a variável é inicializada diretamente com um novo aluno, podemos também usar a palavra-chave var para a declaração da variável como abaixo.

O compilador C# detecta, neste caso, o tipo Aluno para a variável, conforme você atribui uma nova instância de Aluno a ela.  (A palavra-chave var foi introduzida em 2007 com C # 3.0 e .NET Framework 3.5 e funciona para variáveis locais.)


    var  aluno  = new Aluno("Maria", "maria@email.com");          

Nesta sintaxe,  podemos notar que que a expressão new - que é a parte do lado direito do sinal de igual (=) - sempre requer o nome da classe.

A partir do C# 9.0 isso não é mais necessário.

Se o tipo de destino já for conhecido do lado esquerdo do sinal de igual (=) podemos simplificar a declaração omitindo o tipo.

No entanto se você não usar a palavra-chave var no lado esquerdo do sinal de igual (=) o recurso não poderá ser usado.

Então, no C# 9.0, podemos criar uma instância de Aluno usando o construtor padrão da seguinte forma:


    Aluno  aluno  = new();          

Note que não precisamos especificar a classe Aluno do lado direito do sinal de igual (=), o compilador vai inferir o tipo obtendo-o do lado esquerdo do sinal de igual (=).

Podemos também chamar o construtor sobrecarregado da classe Aluno passando um nome e um email:


    Aluno  aluno  = new("Maria", "maria@email.com");          

No entanto, este recurso não pode ser usado quando usamos palavra-chave var para declarar a variável do tipo Aluno porque o compilador não tem como descobrir o tipo que você deseja criar:


    var  aluno  = new("Maria", "maria@email.com")           // NÃO FUNCIONA 

Usando parâmetros opcionais no construtor

Podemos ainda definir a classe Aluno com um construtor com parâmetros opcionais conforme a seguir:

    public class Aluno
    {
        public Aluno(string nome = null , string email =  null )
        {
            Nome = nome;
            Email = email;
        }
        public string Nome { get; set; }
        public string Email { get; set; }
    }

Chamar esse construtor com a nova expressão com tipo de destino funciona exatamente da mesma maneira que faríamos com uma nova expressão clássica, basta omitir o nome da classe no lado direito do sinal de igual (=).

Assim podemos chamar o construtor, por exemplo, sem nenhum argumento:


    Aluno  aluno  = new();         

Ou podemos usar apenas o nome:


    Aluno  aluno  = new("Maria");          

Ou podemos usar o nome e o email:


    Aluno  aluno  = new("Maria", "maria@email.com");          

Podemos também usar os argumentos nomeados para chamar apenas o email:


    Aluno  aluno  = new(email : "maria@email.com");          

Abaixo o código completo do exemplo:

Inicializadores de objeto

Podemos usar este recurso combinado com inicializadores de objeto, o que é realmente útil para inicializar uma coleção sem repetir o tipo em cada linha.

Veja como podemos fazer:

        private List<Aluno> _listaAluno = new List<Aluno>
        {
           new("Maria"),
           new("Pedro"),
           new("Manoel")
        };  

Parâmetros de métodos

Podemos usar este recurso com parâmetros de métodos.

Neste caso o compilador também é capaz de inferir o tipo do contexto de chamada de um método. Ele assumirá automaticamente o tipo dos parâmetros do método.   

Assim se tivermos a classe Aluno como um parâmetro de um método :

  public static void Curso(Aluno aluno)
  {
            //codigo
  }

Podemos usar o recurso desta forma:

 static void Main(string[] args)
 {
      Curso(new("Maria"));
 }

Usando com campos(fields)

Como é impossível declarar um campo com a palavra-chave var, podemos usar este recurso.

Os campos tendem a ter tipos genéricos complexos com nomes longos. Antes do C # 9.0, era tedioso repetir esses tipos em ambos os lados da atribuição do campo.

Antes:


  protected readonly Dictionary<string, Aluno> _listaAlunos = new Dictionary<string, Aluno>();    

 

A partir do C# 9.0 :


  protected readonly Dictionary<string, Aluno> _listaAlunos = new();

 

Outros usos

Este recurso não funciona apenas para new, mas também em outras situações em que o tipo pode ser inferido a partir do contexto.

Um bom exemplo é com objetos que compartilham um tipo base. O tipo base pode ser determinado sem converter os objetos do lado direito em seu tipo base compartilhado.

Assim considerando o código abaixo:

    public abstract class Animal
    {
    }
    public class Cachorro : Animal
    {
        public Cachorro(string raca)    
        {
            Raca = raca;
        }
        public string Raca { get; }
    }

Podemos fazer assim:

 static void Main(string[] args)
 {
      Cachorro cao = new("Lhasa Apso");
 }

Vimos assim diferentes casos de uso para o novo recurso das expressões new com tipo de destino, e podemos indicar que o seu uso facilita bastante a escrita do código especialmente na inicialização direta de campo e propriedade onde a palavra-chave var não é uma opção a considerar.

Pegue o código exemplo :  TargetType_New.zip

"Disse-lhe Jesus: Porque me viste, Tomé, creste; bem-aventurados os que não viram e creram."
João 20:29

Referências:


José Carlos Macoratti