quinta-feira, dezembro 06, 2007

Tipos inferidos pelo compilador

O C# 3.0 possui uma facilidade na declarão das variáveis, essa facilidade se chama de tipo inferido pelo compilador. Agora fica muito mais fácil declarar as variáveis, vc só precisa utilizar a palavra chave var no momento da declaração.

O uso do var não significa que o C# virou uma linguagem não tipada, como uma linguagem de script. Ela apenas facilita a declaração de variáveis, o compilador descobre o seu tipo no momento da sua inicialização. Para isso a declaração e a inicialização precisa ocorrer na mesma linha de código.

Exemplos:

No C# 2.0 vc declarava uma variável assim:

string variavel = "Teste";

No C# 3.0 vc pode declarar assim:

var variavel = "Teste";

Mesmo utilizando var a variável é uma string. Podemos comprovar isso de duas maneiras:

1. Parando com o mouse sobre var mostra que a variável é do tipo System.String.



2. Pela IL gerada pelo compilador C# 3.0

.maxstack 1
.locals init ([0] string variavel)
IL_0000: nop
IL_0001: ldstr "Teste"
IL_0006: stloc.0
IL_0007: ret

O real ganho dos tipos inferidos ocorre quando existe a necessidade de declarar alguma variável para um tipo complexo, por exemplo:

SortedDictionary<string, List> complexo = new SortedDictionary<string, List>();

Com o uso de var a declaração fica assim:



Não podemos esquecer que a declaração e a inicialização deve ocorrer na mesma linha, o código abaixo gera erro de compilação:

var variavel;
variavel = "Teste";

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

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 ;-)


sexta-feira, novembro 30, 2007

Novidades do C#

Nossa... faz muito tempo que eu não posto nada aqui, que vergonha :-(

Mas agora isso vai mudar!!! Pretendo a partir do próximo post colocar informações sobre as novidades da linguagem que eu mais uso no dia-a-dia, o C#.

Não sei se todos já sabem, mas o Visual Studio 2008 foi lançado recentemente e com ele também o veio o .Net Framework 3.5, esse último traz um caminhão de novidades.

Aguardem...

[]'s
Rafael

segunda-feira, março 05, 2007

Pensamento do dia

Não deixe que a programação afete seu alcoolismo.

quinta-feira, novembro 02, 2006

Treinamentos .NET Framework 3.0

A Microsoft disponibilizou alguns treinamentos gratuitos no seu site. Segue o link:
https://www.microsoftelearning.com/eLearning/offerDetail.aspx?offerPriceId=109340

segunda-feira, maio 08, 2006

Apresentação TechEd 2005

A apresentação que eu fiz no TechEd 2005, Novidades do BizTalk 2006, está disponível para download http://download.microsoft.com/download/e/4/e/e4e840c1-66a2-48e0-926a-798f2d18ab32/BAP_RafaelGodinho.zip

As outras apresentações do evento podem ser baixadas em http://www.msdnbrasil.com.br/secure/teched/Default.aspx