domingo, dezembro 02, 2007

Propriedades autoimplementadas

Muitas vezes quando vamos criar uma propriedade em código C# nos deparamos com um padrão repetitivo e trivial, o get retorna o valor de uma variável privada e o set grava o valor em uma propriedade privada, como por exemplo o código abaixo:

class Cliente
{
private string m_Nome;
public string Nome
{
get { return m_Nome; }
set { m_Nome = value; }
}

private string m_Sobrenome;
public string Sobrenome
{
get { return m_Sobrenome; }
set { m_Sobrenome = value; }
}
}

Abaixo segue trecho que trata da propriedade Nome na IL (Intermediate Language) gerada:

.field private string m_Nome
.method public hidebysig specialname instance string
get_Nome() cil managed
{
// Code size 12 (0xc)
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld string NovidadesCSharp.Cliente::m_Nome
IL_0007: stloc.0
IL_0008: br.s IL_000a

IL_000a: ldloc.0
IL_000b: ret
} // end of method Cliente::get_Nome

.method public hidebysig specialname instance void
set_Nome(string 'value') cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: stfld string NovidadesCSharp.Cliente::m_Nome
IL_0008: ret
} // end of method Cliente::set_Nome

Para ajudar os programadores, o C# 3.0 (o .Net Framework está na versão 3.5, mas a linguagem C# está na versão 3.0) possui uma nova funcionalidade chamada de propriedade autoimplementada (tradução livre), ou Automatically Implemented Properties em inglês.

Com ela o programador não precisa se preocupar em criar os campos privados, ler e gravar os seus valores. Tudo o que precisa ser feito é declarar a propriedade. Exemplo:

class Cliente
{
public string Nome { get; set; }
public string Sobrenome { get; set; }
}

Bem mais simples, não é mesmo?

Até a IL fica parecida. As principal diferença está no uso do campo gerado automaticamente pelo compilador <nome>k__BackingField, isso pode ser verificado pelo uso do atributo CompilerGeneratedAttribute:

.field private string '<nome>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname instance string
get_Nome() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 11 (0xb)
.maxstack 1
.locals init (string V_0)
IL_0000: ldarg.0
IL_0001: ldfld string NovidadesCSharp.Cliente::'<nome>k__BackingField'
IL_0006: stloc.0
IL_0007: br.s IL_0009

IL_0009: ldloc.0
IL_000a: ret
} // end of method Cliente::get_Nome

.method public hidebysig specialname instance void
set_Nome(string 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld string NovidadesCSharp.Cliente::'<nome>k__BackingField'
IL_0007: ret
} // end of method Cliente::set_Nome

Se for necessário criar uma propriedade somente de leitura, ou somente de escrita, também é possível indicar modificadores de acessibilidade no get ou no set:

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

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

No exemplo acima, a propriedade Codigo é somente para leitura e deve ser inicializada pelo construtor.

Se vcs tiverem alguma dúvida é só mandar um comentário ;-)


Nenhum comentário: