Como passar tipos anônimos como parâmetros?

143

Como posso passar tipos anônimos como parâmetros para outras funções? Considere este exemplo:

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);

A variável queryaqui não tem um tipo forte. Como devo definir minha LogEmployeesfunção para aceitá-la?

public void LogEmployees (? list)
{
    foreach (? item in list)
    {

    }
}

Em outras palavras, o que devo usar em vez de ?marcas.

Saeed Neamati
fonte
1
Melhor pergunta duplicada diferente que lida com a passagem de parâmetros em vez de retornar dados: stackoverflow.com/questions/16823658/…
Rob Church

Respostas:

183

Eu acho que você deveria fazer uma aula para esse tipo anônimo. Isso seria a coisa mais sensata a se fazer na minha opinião. Mas se você realmente não quiser, poderá usar a dinâmica:

public void LogEmployees (IEnumerable<dynamic> list)
{
    foreach (dynamic item in list)
    {
        string name = item.Name;
        int id = item.Id;
    }
}

Observe que isso não é fortemente digitado; portanto, se, por exemplo, Name mudar para EmployeeName, você não saberá que há um problema até o tempo de execução.

Tim S.
fonte
Eu verifiquei isso como resposta correta, por causa do dynamicuso. Eu realmente vim a calhar para mim. Obrigado :)
Saeed Neamati
1
Concordo que, uma vez que os dados começam a ser transmitidos, uma maneira mais estruturada pode / normalmente deve ser preferida para não introduzir bugs difíceis de encontrar (você está evitando o sistema de tipos). No entanto, se você deseja encontrar um compromisso, outra maneira é simplesmente passar um dicionário genérico. Os inicializadores de dicionário em C # são bastante convenientes de usar atualmente.
Jonas
Há alguns casos em que você deseja uma implementação genérica e passar tipos físicos significa possivelmente alternar ou implementar a fábrica que começa a inchar o código. Se você tem uma situação verdadeiramente dinâmica e não se importa com um pouco de reflexão para lidar com os dados que está recebendo, isso é perfeito. Obrigado pela resposta @ Tim S.
Larry Smith
42

Você pode fazer assim:

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
    foreach (T item in list)
    {

    }
}

... mas você não poderá fazer muito com cada item. Você pode ligar para o ToString, mas não poderá usar (por exemplo) Namee Iddiretamente.

Jon Skeet
fonte
2
Exceto que você pode usar where T : some typeno final da primeira linha para restringir o tipo. Nesse ponto, porém, esperar um certo tipo de interface comum faria mais sentido esperar uma interface. :)
CassOnMars
9
@d_r_w: Você não pode usar where T : some typecom tipos anônimos, porém, como eles não implementar qualquer tipo de interface de ...
Jon Skeet
@ dlev: Você não pode fazer isso, porque foreach exige que a variável iterada no implementar GetEnumerator, e tipos anônimos não garantam isso.
CassOnMars
1
@ Jon Skeet: bom ponto, meu cérebro está com pouca potência esta manhã.
CassOnMars
1
@JonSkeet. Suponho que você possa usar a reflexão para ainda acessar / definir as propriedades se T for um tipo anônimo, certo? Estou pensando em um caso em que alguém escreve uma instrução "Select * from" e usa uma classe anônima (ou definida) para definir quais colunas do resultado da consulta são mapeadas para as mesmas propriedades nomeadas no seu objeto anônimo.
precisa saber é o seguinte
19

Infelizmente, o que você está tentando fazer é impossível. Sob o capô, a variável de consulta é digitado para ser um IEnumerablede um tipo anônimo. Nomes de tipos anônimos não podem ser representados no código do usuário, portanto, não há como torná-los um parâmetro de entrada para uma função.

Sua melhor aposta é criar um tipo e usá-lo como o retorno da consulta e depois passá-lo para a função. Por exemplo,

struct Data {
  public string ColumnName; 
}

var query = (from name in some.Table
            select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);

Nesse caso, porém, você está selecionando apenas um único campo; portanto, pode ser mais fácil selecionar o campo diretamente. Isso fará com que a consulta seja digitada como IEnumerabledo tipo de campo. Nesse caso, nome da coluna.

var query = (from name in some.Table select name);  // IEnumerable<string>
JaredPar
fonte
Meu exemplo foi um, mas na maioria das vezes é mais. Sua resposta através de obras (e bastante óbvia agora). Eu só precisava de uma pausa para o almoço a pensar que embora ;-)
Tony Trembath-Drake
Uma ressalva é que, quando você cria uma classe adequada, Equalsmuda o comportamento. Ou seja, você tem que implementá-lo. (Eu sabia dessa discrepância, mas ainda conseguiu esquecê-lo durante uma refatoração.)
LosManos
11

Você não pode passar um tipo anônimo para uma função não genérica, a menos que o tipo de parâmetro seja object.

public void LogEmployees (object obj)
{
    var list = obj as IEnumerable(); 
    if (list == null)
       return;

    foreach (var item in list)
    {

    }
}

Tipos anônimos destinam-se ao uso de curto prazo dentro de um método.

Do MSDN - Tipos anônimos :

Você não pode declarar um campo, uma propriedade, um evento ou o tipo de retorno de um método como tendo um tipo anônimo. Da mesma forma, você não pode declarar um parâmetro formal de um método, propriedade, construtor ou indexador como tendo um tipo anônimo. Para passar um tipo anônimo ou uma coleção que contém tipos anônimos, como argumento para um método, você pode declarar o parâmetro como objeto de tipo . No entanto, fazer isso anula o objetivo da digitação forte.

(ênfase minha)


Atualizar

Você pode usar genéricos para alcançar o que deseja:

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}
Oded
fonte
4
Se você não pudesse passar tipos anônimos (ou coleções de um tipo anônimo) para métodos, o LINQ inteiro falharia. Você pode, é que o método precisa ser totalmente genérico, sem usar as propriedades do tipo anônimo.
Jon Skeet
2
re object- ou dynamic; p
Marc Gravell
Se fundição com "como" você deve verificar se a lista é nulo
Alex
"pode"! = "ter que". Usar objectnão é o mesmo que tornar um método genérico no tipo anônimo, conforme minha resposta.
91311 Jon Skeet
8

Normalmente, você faz isso com genéricos, por exemplo:

MapEntToObj<T>(IQueryable<T> query) {...}

O compilador deve inferir o Tquando você chama MapEntToObj(query). Não tenho certeza do que você deseja fazer dentro do método, então não sei dizer se isso é útil ... o problema é que, MapEntToObjainda dentro, você não pode nomear o nome T- você pode:

  • chame outros métodos genéricos com T
  • use a reflexão Tpara fazer as coisas

mas, além disso, é muito difícil manipular tipos anônimos - principalmente porque são imutáveis ​​;-p

Outro truque (ao extrair dados) é também passar um seletor - ou seja, algo como:

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
Marc Gravell
fonte
1
Aprendeu algo novo, não sabia que tipos anônimos são imutáveis! ;)
Annie Lagang
1
@AnneLagang que realmente depende do compilador, pois os gera. No VB.NET, os tipos anon podem ser mutáveis.
Marc Gravell
1
FYI: mesclado de stackoverflow.com/questions/775387/…
Shog9
7

Você pode usar genéricos com o seguinte truque (conversão para o tipo anônimo):

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {
        var typedItem = Cast(item, new { Name = "", Id = 0 });
        // now you can use typedItem.Name, etc.
    }
}

static T Cast<T>(object obj, T type)
{
    return (T)obj;
}
Stanislav Basovník
fonte
6

"dinâmico" também pode ser usado para esse fim.

var anonymousType = new { Id = 1, Name = "A" };

var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };

private void DisplayAnonymousType(dynamic anonymousType)
{
}

private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
   foreach (var info in anonymousTypes)
   {

   }
}
Dinesh Kumar P
fonte
1
Esta é a resposta certa! Ele só precisa de mais amor :)
Korayem
2

Em vez de passar um tipo anônimo, passe uma lista de um tipo dinâmico:

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. Assinatura do método: DoSomething(List<dynamic> _dynamicResult)
  3. Método de chamada: DoSomething(dynamicResult);
  4. feito.

Obrigado a Petar Ivanov !

usefulBee
fonte
0

Se você sabe que seus resultados implementam uma certa interface, você pode usá-la como tipo de dados:

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}
Alex
fonte
0

Eu usaria IEnumerable<object>como tipo para o argumento. No entanto, não é um grande ganho para o elenco explícito inevitável. Felicidades

Mario Vernari
fonte