.NET

15 mai, 2009

Usando tipos anônimos como parâmetro

Publicidade

Quando a Microsoft anunciou os tipos
anônimos (Anonymous Types) eu fiquei bastante contente, era possível
criar tipos rapidamente de uma forma muito fácil. Para quem ainda não
conhece essa nova funcionalidade aqui vai o link.

Olhando a sintaxe de construção de um tipo anônimo logo associei aos
arrays associativos das linguagens de script, conhecidos como Hash. O C# sempre teve o objeto Hashtable, mas não é flexível comparando com linguagens como Ruby ou Javascript. Só pra exemplificar o que estou dizendo:

No Ruby posso chamar um método passando um hash como parâmetro, sintaxe bastante utilizada no Ruby on Rails, dessa forma:

link_to("Profile", :controller => "profiles", :action => "show")
link_to("Profile", :controller => "profiles", :id => @id)

Nas duas linhas de exemplo os dois últimos itens formam uma Hashtable, no caso do Ruby se chama Hash.
Do lado do método também fica fácil de capturar o que foi passado. Com
tipos anônimos do C# podemos fazer uma sintaxe bem próxima, que ficaria
assim:

LinkTo("Profile", new { Controller = "profiles", Action = "show" });
LinkTo("Profile", new { Controller = "profiles", Id = id });

Mas por que utilizar essa forma de passar parâmetros?

É elegante e limpo em alguns casos. Alguns métodos realizam funções
onde é necessário informar MUITOS parâmetros para que se tenha um bom
nível de customização da ação que será realizada. E como alguns
parâmetros são opcionais acabamos tendo que criar várias sobrecargas
para o mesmo método, a fim de facilitar a vida de quem vai usar.

No exemplo acima estamos utilizando um método cria um link HTML em
um ambiente MVC. Como são muitos parâmetros (texto, controller, action,
style, class, id, querystring) e raramente usaremos todos, utilizamos
um tipo anônimo para informar os parâmetros “opcionais”. Assim não
precisaremos encher a nossa classe com sobrecargas do método LinkTo.

Outro ganho que temos é na hora de adicionar mais um parâmetro ao
método. Adicionamos no tipo anônimo e não precisamos alterar as
chamadas na aplicação inteira. Mesmo com refactoring isso às vezes é
problemático.

Ok! Agora, como crio meu próprio método?

Para ver como funciona vamos implementar um método que cria links,
parecido com o que exemplifiquei antes. Primeiro veremos como fica o
método que vou chamar de “CriaLink”, depois explico o que ele está
fazendo:

public static object GetPropertyValue(this object obj, string propertyName)
{
try
{
return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
catch
{
return null;
}
}
public string CriaLink(string titulo, object parametros)
{
var para = (string) parametros.GetPropertyValue("Para");
var descricao = (string) parametros.GetPropertyValue("Descricao");
var borda = (int?) parametros.GetPropertyValue("Borda");

var linkBuilder = new StringBuilder();

linkBuilder.Append("<a");

if(!String.IsNullOrEmpty(para))
{
linkBuilder.AppendFormat(" href='{0}'", para);
}

if(!String.IsNullOrEmpty(descricao))
{
linkBuilder.AppendFormat(" title='{0}'", descricao);
}

if(borda != null && borda > 0)
{
linkBuilder.AppendFormat(" style='border: {0}px solid silver'", borda);
}

linkBuilder.AppendFormat(">{0}</a>", titulo);

return linkBuilder.ToString();
}
CriaLink("Google", new { Para = "http://www.google.com", Descricao = "Clique aqui para acessar o Google.", Borda = 2 });
// <a href='http://www.google.com' title='Clique aqui para acessar o Google.' style='border: 2px solid silver'>Google</a>

CriaLink("Globo", new { Para = "http://www.globo.com", Borda = 2 });
// <a href='http://www.globo.com' style='border: 2px solid silver'>Globo</a>

CriaLink("Home", new { Para = "/Index.html", Descricao = "Voltar para a página inicial." });
// <a href='/Index.html' title='Voltar para a página inicial.'>Home</a>

Criei um extension method de nome GetPropertyValue na classe Object para quem não sabe do que se trata extension methods falei sobre isso aqui.
Esse método busca o valor da propriedade de qualquer objeto passando
apenas seu nome. Isso vai ser muito útil para tratar os tipos anônimos.

Já na implementação do método “CriaLink” primeiramente busco os
valores das possíveis propriedades atribuídas ao tipo anônimo passado
por parâmetro, que é do tipo Object porque não é possível prever um
tipo anônimo. O método GetPropertyValue já se responsabiliza por saber se existe ou não a propriedade, no caso de ela não existir o resultado é null.

Depois fica mais simples. Apenas criei uma StringBuilder para
concatenar a tag de link que vou criar. Testo se as variáveis
provenientes dos parâmetros possuem valores e concateno o valor
relativo a cada propriedade.

Por último, mostro como podemos utilizar o método que criamos e o resultado obtido.

Já falei aqui o quanto sou fã da flexibilidade das linguagens de
script. O C# tem incorporado algumas funcionalidades dessas linguagens
e, mesmo assim, ao contrário do que eu imaginava, consegue manter sua
característica de ser fortemente tipado. Temos que aproveitar isso e
tornar nosso código mais inteligente.

Espero que tenham gostado e qualquer duvida ou sugestão é só entrar em contato ou comentar aqui mesmo. Até mais.