segunda-feira, dezembro 03, 2007

Inicializadores de objetos e coleções

No C# 2.0 ou anterior, para que um objeto fosse instanciado e tivesse seus atributos inicializados era necessário declarar o objeto e setar os valores de cada atributo individualmente, como no exemplo abaixo (que está aproveitando a classe cliente do post anterior):

class Cliente
{
public int Codigo { get; private set; }
public string Nome { get; set; }
public string Sobrenome { get; set; }

public Cliente(int codigo)
{
Codigo = codigo;
}
}

Cliente cliente = new Cliente(1);
cliente.Nome = "Rafael";
cliente.Sobrenome = "Godinho";

A IL gerada vai ser assim:

// Code size 33 (0x21)
.maxstack 2
.locals init ([0] class NovidadesCSharp.Cliente cliente)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: newobj instance void NovidadesCSharp.Cliente::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldstr "Rafael"
IL_000e: callvirt instance void NovidadesCSharp.Cliente::set_Nome(string)
IL_0013: nop
IL_0014: ldloc.0
IL_0015: ldstr "Godinho"
IL_001a: callvirt instance void NovidadesCSharp.Cliente::set_Sobrenome(string)
IL_001f: nop
IL_0020: ret

Para facilitar a vida o C# 3.0 possui uma sintaxe que combina a declaração do objeto e a sua inicialização em um único passo. Para isso a inicialização dos atributos deve ser feita após a criação do objeto dentro de {} e separando os atributos por vírgula, exemplo:

Cliente cliente = new Cliente(1) { Nome = "Rafael", Sobrenome = "Godinho"};

E a IL gerada é bem parecida, a única diferença é que o compilador gera duas entradas na memória para a mesma instância do objeto, como vcs podem ver:

// Code size 35 (0x23)
.maxstack 2
.locals init ([0] class NovidadesCSharp.Cliente cliente,
[1] class NovidadesCSharp.Cliente '<>g__initLocal0')
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: newobj instance void NovidadesCSharp.Cliente::.ctor(int32)
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldstr "Rafael"
IL_000e: callvirt instance void NovidadesCSharp.Cliente::set_Nome(string)
IL_0013: nop
IL_0014: ldloc.1
IL_0015: ldstr "Godinho"
IL_001a: callvirt instance void NovidadesCSharp.Cliente::set_Sobrenome(string)
IL_001f: nop
IL_0020: ldloc.1
IL_0021: stloc.0
IL_0022: ret

Para coleções a idéia é basicamente a mesma, os únicos requisitos são: a coleção precisa implementar System.Collections.Generics.IEnumerable e precisa ter o método Add público, exemplo:

List<Cliente> clientes = new List<Cliente>
{
new Cliente(1) { Nome = "Rafael", Sobrenome = "Godinho" },
new Cliente(2) { Nome = "Melissa", Sobrenome = "Godinho" },
new Cliente(3) { Nome = "Somente Nome" }
};

Pelo exemplo acima, podemos perceber que não há necessidade de informar todos os atributos na inicialização de objetos, somente os que forem pertinentes, como é o caso da terceira instância de cliente. Os atributos não informados receberão o valor padrão do seu tipo de dado de acordo com o .Net Framework.

Os parênteses são opcionais se o construtor sem parâmetros do objeto for chamado. Eu poderia ter escrito tanto new List<Cliente> {...} como new List<Cliente>() {...}.

É isso aí, amanhã tem mais post da série de novidades do C# 3.0.

[]'s
Rafael

Um comentário:

Anônimo disse...

Não se atualiza mais esse blog? rs