Os tipos anônimos são uma “mão na roda” quando queremos criar objetos de uma classe (que não precisa ser criada previamente) com propriedades a partir do resultado de uma consulta em LINQ. O problema começa quando saímos do escopo dos objetos anônimos criados, por exemplo, quando atribuímos os mesmos em um tipo object e retornamos em um método.
Como o compilador gera o nome para a classe do objeto anônimo (algo parecido com isso: <>f__AnonymousType0`2) não é possível fazer o cast utilizando o mesmo, então qual a solução?
Para mostrar um exemplo do cenário descrito vamos utilizar uma aplicação que basicamente cria uma lista de pessoas, e como resultado de uma consulta retorna um objeto anônimo com alguns dados da pessoa solicitada. Vamos criar um aplicação ASP.NET Web Forms:
Primeiro criamos as classes abaixo:
namespace Modelo
{
public class Pessoa
{
public int id { get; set; }
public string nome { get; set; }
public DateTime dataNascimento { get; set; }
public Telefone telefone { get; set; }
public static IEnumerable<Pessoa> Carregar()
{
return new List<Pessoa>()
{
new Pessoa() { id = 1, nome = "João", dataNascimento =
DateTime.Today.AddYears(-20),
telefone = new Telefone() { id = 1, ddd = "43", numero =
"11112222" }},
new Pessoa() { id = 2, nome = "Maria", dataNascimento =
DateTime.Today.AddYears(-30),
telefone = new Telefone() { id = 2, ddd = "44", numero =
"22224444" }},
new Pessoa() { id = 3, nome = "Aline", dataNascimento =
DateTime.Today.AddYears(-40),
telefone = new Telefone() { id = 3, ddd = "45", numero =
"33334444" }},
new Pessoa() { id = 4, nome = "José", dataNascimento =
DateTime.Today.AddYears(-10),
telefone = new Telefone() { id = 4, ddd = "46", numero =
"55556666" }},
new Pessoa() { id = 5, nome = "Fernanda", dataNascimento =
DateTime.Today.AddYears(-15),
telefone = new Telefone() { id = 5, ddd = "47", numero =
"77778888" }},
new Pessoa() { id = 6, nome = "Carlos", dataNascimento =
DateTime.Today.AddYears(-30),
telefone = new Telefone() { id = 6, ddd = "48", numero =
"99999999" }}
};
}
public static object Pesquisar(int id)
{
var Pessoas = Carregar();
var resultado = (from p in Pessoas
where p.id == id
select new
{
DataNascimento = p.dataNascimento,
Telefone = string.Format("({0}) {1}",
p.telefone.ddd,p.telefone.numero)
}).Single();
return resultado;
}
}
public class Telefone
{
public int id { get; set; }
public string ddd { get; set; }
public string numero { get; set; }
}
}
Entendendo o código:
Criamos as classes Pessoa e Telefone. A classe Pessoa contém um método chamado Carregar que retorna uma lista com alguns objetos do tipo Pessoa. Também criamos um método Pesquisar que recebe por parâmetro um identificador e retorna o objeto correspondente da lista. O que deve ser verificado nesse método é que o mesmo retorna um objeto anônimo com duas propriedades: DataNascimento e Telefone. Repare que o tipo de retorno é um object, logo, será necessário um cast para o tipo original para acesso às propriedades.
Agora vamos criar nosso ASPX que deverá conter um componente DropDownList para receber a lista de Pessoas, ao selecionar uma das opções o controle executará um PostBack para resgatar as informações de Data de Nascimento e Telefone e apresentar as mesmas para o usuário.
<p>Pessoas:</p>
<asp:DropDownList ID="dropPessoas" runat="server" DataTextField="nome"
DataValueField="id" AutoPostBack="True"
OnSelectedIndexChanged="dropPessoas_SelectedIndexChanged" />
<p><asp:Literal ID="liDataNascimento" runat="server" /></p>
<p><asp:Literal ID="liTelefone" runat="server" /></p>
E o Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
CarregarDropDown();
}
void CarregarDropDown()
{
dropPessoas.DataSource = Pessoa.Carregar();
dropPessoas.DataBind();
}
protected void dropPessoas_SelectedIndexChanged(object sender, EventArgs e)
{
object x = Pessoa.Pesquisar(Convert.ToInt32(dropPessoas.SelectedValue));
}
Note que no evento “dropPessoas_SelectedIndexChanged” chamamos o método Pesquisar passando o id da pessoa selecionada, o problema é, como acessamos a propriedades DataNascimento e Telefone?
Primeiro vamos criar o método que consegue fazer o cast para o tipo anônimo criado:
protected T Cast<T>(object obj, T tipo)
{
return (T)obj;
}
Entendendo o código:
Criamos um método genérico que recebe um objeto a ser convertido, e um segundo parâmetro que receberá um objeto do mesmo tipo para o qual queremos converter o objeto do primeiro parâmetro, faremos isso para podermos fazer o compilador inferir o tipo em T e fazer o cast necessário.
Agora você vai perguntar, mas como vou passar um objeto do mesmo tipo anônimo criado pela minha consulta LINQ?
A resposta está na criação de outro objeto anônimo com as mesmas propriedades criadas para o objeto retornado na consulta LINQ, para o compilador, 2 objetos anônimos com as mesmas propriedades são da mesma classe anônima:
protected void dropPessoas_SelectedIndexChanged(object sender, EventArgs e)
{
object x = Pessoa.Pesquisar(Convert.ToInt32(dropPessoas.SelectedValue));
var obj = Cast(x, new { DataNascimento = DateTime.Now, Telefone = "" });
liDataNascimento.Text = obj.DataNascimento.ToShortDateString();
liTelefone.Text = obj.Telefone;
}
Note que chamamos o método Cast passando primeiro o objeto que queremos converter, e em segundo passamos um novo objeto anônimo com as mesmas propriedades (os valores não importam) do objeto sendo apontado por x. Dessa forma a variável obj será do tipo anônimo desejado e as propriedades DataNascimento e Telefone estarão acessíveis.
Chamamos essa técnica de “Cast por Exemplo”, pois ajudamos o compilador a inferir um tipo passando outro objeto com a mesma estrutura como exemplo.
Com o C# 4.0 podemos usar os tipos dinâmicos para facilitar essa tarefa como mostrado abaixo:
protected void dropPessoas_SelectedIndexChanged(object sender, EventArgs e)
{
dynamic x = Pessoa.Pesquisar(Convert.ToInt32(dropPessoas.SelectedValue));
liDataNascimento.Text = x.DataNascimento.ToShortDateString();
liTelefone.Text = x.Telefone;
}
Como o tipo dinâmico só é testado em tempo de execução, o compilador irá descobrir que aquela instância contém as propriedades informadas e irá resgatar os valores normalmente.
Conclusão
Tipos anônimos, genéricos e tipos dinâmicos são pequenos exemplos das possibilidades que o C# nos oferece, é importante conhecer a fundo esses recursos para o máximo aproveitamento em diversas situações.