Um tipo anônimo pode ser declarado da seguinte maneira:
var c = new { Nome = "Ari", Idade = 28 };
Neste caso a variável c é um tipo de possui duas propriedades: uma propriedade do tipo System.String chamada Nome e uma propriedade do tipo System.Int32 chamada Idade.
Os tipos anônimos são muito úteis quando realizamos consultas usando o LINQ pois normalmente temos que retornar um tipo com um conjunto de propriedades diferente dos tipos já existentes. Por exemplo, vejam as duas consultas abaixo:
var q1 = from c in dc.Customers where c.City == "London" select new { c.FirstName, c.LastName, c.City }; var q2 = from c in dc.Customers where c.City == "London" select new { c.FirstName, c.City };
A primeira consulta retorna um tipo que contém 3 propriedades (FirstName, LastName e City) e a segunda um tipo com 2 propriedades (FirstName e City). Sem os tipos anônimos teríamos que definir explicitamente duas classes para representar os tipos retornados.
Mas, se o tipo é anônimo, como podemos fazer para um método retorná-lo?
public ???? ListCustomers() { var q1 = from c in dc.Customers where c.City == "London" select new { c.FirstName, c.LastName, c.City }; return q1; }
Bom, podemos resolver essa questão pensando da seguinte maneira: se um tipo anônimo é um tipo do .NET, então esse herda de System.Object, vamos então retornar um object.
public object ListCustomers() { ... }
Esse raciocínio está correto, o problema é que transformando para object perdemos a possibilidade de utilizarmos as propriedades do tipo anônimo diretamente (strong typing).
var c = ListCustomers(); c.FirstName = "Anders"; // erro
Bom, para resolver esse problema temos 3 possíveis soluções:
- Reflection.
- Dynamic Programming.
- Realizar um cast do object com o tipo anônimo.
A primeira opção é lenta e muito código deve ser criado, portanto será descartada. A segunda opção é interessante mas vou limitar meu escopo com as versões anteriores à 4.0. Vou mostrar então a terceira opção.
Para realizarmos um cast de um object para um tipo anônimo podemos, com auxílio de Generics, criar o método abaixo:
public T CastObjectToType<T>(object objeto, T tipo) { return (T)objeto; }
Podemos então converter o objeto em um tipo anônimo da seguinte maneira:
object o = ListCustomers(); var c = CastObjectToType(o, new { FirstName = String.Empty, LastName = String.Empty, City = String.Empty}); c.FirstName = "Anders"; // OK
Isso é possível graças à inferência de tipo, que nos permite fazer um cast sem conhecer o nome do tipo. É o que foi realizado no método CastObjectToType.
Finalizado, a resposta é SIM. Podemos retornar um tipo anônimo de um método.
Links de ajuda:
Anonymous Types (C# Programming Guide)
http://bit.ly/69HYJd
Generic Methods (C# Programming Guide)
http://bit.ly/6wFbas
Getting Started with LINQ in C#
http://bit.ly/4BOIUU
Parabéns pelo artigo, me ajudou bastante.
ResponderExcluirGostei, põe isso no codeproject (se é que não está ainda)
ResponderExcluirHenrry,
ResponderExcluirNo CodeProject já existem alguns artigos parecidos.
Converting anonymous types to any type
http://bit.ly/b856Z7
Casting and Passing Anonymous Types
http://bit.ly/aFlt1f
Obrigado pelo feedback...
Um abraço.
Oi, muito bom o artigo, gostaria de saber como fazer o mesmo com a segunda opção (Dynamic Programming).
ResponderExcluirOlá Ari,
ResponderExcluirApenas um comentário.
No caso do exemplo, o que ocorre é um upcast (para object) e um downcast (para o anonymous type). Isto é necessário, pois o anonymous type não é exportado publicamente. Mas fazendo assim você paga um preço de tempo de execução.
Então este caso seria mais eficiente vc ter um tipo não anonimo para ser exportado e resolvido em tempo de compilação.
class TipoNaoAnonimo //Escolher Um Nome Melhor
{
public string FirstName{ get; internal set; }
public string LastName{ get; internal set; }
public string City { get; internal set; }
}
e vc faz a projeção a partir deste tipo.
public IEnumerable ListCustomers()
{
var q1 = from c in dc.Customers
where c.City == "London"
select new TipoNaoAnonimo{ c.FirstName, c.LastName, c.City };
return q1;
}
e seu respectivo consumo
var c = ListCustomers().First();
c.FirstName = "Anders"; // OK
Obrigado pelo comentário.
ResponderExcluirUm abraço!