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

    }
}
Listagem 1. Classes criadas no exemplo

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>
Listagem 2. Conteúdo da página ASPX

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));                
}
Listagem 3. Code behind da página

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;
}
Listagem 4. Método para realização do cast

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;          
}
Listagem 5. Criando objetos da classe anônima

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;
}
Listagem 6. Utilização de tipo dinâmico

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.