Uma lista genérica de classe anônima

416

No C # 3.0, você pode criar classe anônima com a seguinte sintaxe

var o = new { Id = 1, Name = "Foo" };

Existe uma maneira de adicionar essas classes anônimas a uma lista genérica?

Exemplo:

var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };

List<var> list = new List<var>();
list.Add(o);
list.Add(o1);

Outro exemplo:

List<var> list = new List<var>();

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}
DHornpout
fonte
2
Observe que todos os objetos devem ser digitados da mesma forma na matriz. Raramente você pode precisar de ajuda com um elenco, especialmente para valores nulosnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS
1
tipos anônimos são projetados para serem usados ​​como armazenamento temporário; na maioria dos casos, você os cria na instrução de seleção LINQ usando Select (i => new {i.ID, i.Name}); que retornaria um IEnumerable do tipo correto se você redefinir sua cláusula while em uma instrução LINQ.Where, você nunca precisará da lista e, se o fizer, poderá simplesmente chamar o ToList nela
MikeT

Respostas:

427

Você poderia fazer:

var list = new[] { o, o1 }.ToList();

Existem várias maneiras de esfolar esse gato, mas basicamente todas elas usam inferência de tipo em algum lugar - o que significa que você precisa chamar um método genérico (possivelmente como um método de extensão). Outro exemplo pode ser:

public static List<T> CreateList<T>(params T[] elements)
{
     return new List<T>(elements);
}

var list = CreateList(o, o1);

Você entendeu a ideia :)

Jon Skeet
fonte
32
@ DHornpout: Isso daria uma matriz, não uma lista <T>.
Jon Skeet
23
@DHornpout: Você tem "using System.Linq;" no topo do seu arquivo? ToList é um operador LINQ.
Jon Skeet
5
Entendi .. Precisa incluir "using System.Linq". Obrigado.
DHornpout 4/03/09
2
Parece uma inconsistência no Visual Studio, que o intellisense não é mais útil para descobrir inclusões ausentes de assemblies com métodos de extensão referenciados (o mesmo que tipos referenciados).
LOAS
3
este homem está em toda parte, pesquisou 8 perguntas hoje, 7 responderam por ele.
Kugan Kumar
109

Aqui está a resposta.

string result = String.Empty;

var list = new[]
{ 
    new { Number = 10, Name = "Smith" },
    new { Number = 10, Name = "John" } 
}.ToList();

foreach (var item in list)
{
    result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}

MessageBox.Show(result);
Dutt
fonte
12
Dutt, seu código deve funcionar sem o .ToList () no final.
DHornpout 4/11/2009
3
tudo bem legal, agora precisamos de um exemplo de substituição das novas linhas {} por uma instrução select. var lista = sourceList.Select (o => novo {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
Topwik
@towpse alguma solução sobre isso?
Kiquenet
@ Dutt, qualquer exemplo se eu usar um método (função) que retorna uma lista <T>?
Kiquenet
Agora há string.Joininterpolação de método e string, portanto não há necessidade de usar foreache Format.
realsonic 4/08/16
61

Existem várias maneiras de fazer isso, mas algumas das respostas aqui estão criando uma lista que contém elementos de lixo, o que requer que você limpe a lista.

Se você estiver procurando por uma lista vazia do tipo genérico, use um Selecionar em uma lista de tuplas para fazer a lista vazia. Nenhum elemento será instanciado.

Aqui está uma lista para criar uma lista vazia:

 var emptyList = new List<Tuple<int, string>>()
          .Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();

Em seguida, você pode adicionar usando seu tipo genérico:

 emptyList.Add(new { Id = 1, Name = "foo" });
 emptyList.Add(new { Id = 2, Name = "bar" });

Como alternativa, você pode fazer algo como abaixo para criar a lista vazia (mas prefiro o primeiro exemplo, porque você pode usá-lo também para uma coleção povoada de Tuplas):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   
Paul Rouleau
fonte
1
Eu gosto muito desse jeito. Obrigado Paul! É sempre um bom dia para usar as tuplas! xD
Brady Liles
Eu gosto disso. O seu bom ter algum tipo de declaração concreta do objeto que eu vou passar em.
Morvael
Bom eu só código de escrita final que incluiu limpando a lista tempo para reescrevê-lo
JoshBerke
Obrigado pela ideia. Uma sugestão: você pode evitar a alocação de uma lista fictícia se você usarEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe
45

Não exatamente, mas você pode dizer List<object>e as coisas vão funcionar. No entanto, list[0].Idnão vai funcionar.

Isso funcionará em tempo de execução no C # 4.0 com um List<dynamic>, ou seja, você não obterá o IntelliSense.

Jeff Moser
fonte
Porém, ele não é fortemente tipado, no sentido de que você não terá suporte ao compilador intellisense para os itens da lista.
Joel Coehoorn
31
Esse é o tipo de coisa que temo que as pessoas façam com dinâmica.
erikkallen
2
Eu não disse que era uma ótima idéia, mas que era possível :-) Poderia ser necessário armazenar objetos do Ruby, por exemplo.
10139 Jeff Moser
2
Mas, nesses casos, o tipo de fonte é dinâmico, afinal, não faz sentido usar uma Lista <dinâmica> para tipos anônimos.
Dykam 30/10/09
1
Muito útil. Especialmente se a lista tiver que ser definida antes que itens anônimos sejam adicionados a ela.
Karlth 8/07/2014
24

eu acho

List<T> CreateEmptyGenericList<T>(T example) {
    return new List<T>();
}

void something() {
    var o = new { Id = 1, Name = "foo" };
    var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}

vai funcionar.

Você também pode escrever dessa forma:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
erikkallen
fonte
Sim, esta solução ajudará a resolver a inicialização da matriz anônima. Obrigado.
DHornpout 4/03/09
1
Basta colocar um pouco de <T> após o nome do método.
Martin
21

Eu costumo usar o seguinte; principalmente porque você "começa" com uma lista vazia.

var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.

Ultimamente, eu tenho escrito assim:

var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );

O uso do método repeat também permitiria:

var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });

..que fornece a lista inicial com o primeiro item já adicionado.

Rostov
fonte
2
Você não precisa começar com uma lista vazia - você pode fazer Range (0,1) e tornar seu primeiro objeto na instrução select o primeiro objeto.
Matthew M.
1
Você não precisa começar com uma lista vazia - no caso de saber qual é o primeiro item (como no exemplo), então você está certo no seu comentário. Muitas vezes, embora eu use isso para analisar um arquivo / fonte de dados intermediário e não acesse o primeiro item verdadeiro até usá-lo em um cenário de projeção LINQ (e, portanto, não preciso dar conta de ignorar o primeiro registro).
Rostov
19

Você pode fazer isso no seu código.

var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
MalachiteBR
fonte
11

Na versão mais recente 4.0, pode usar dinâmico como abaixo

var list = new List<dynamic>();
        list.Add(new {
            Name = "Damith"
    });
        foreach(var item in list){
            Console.WriteLine(item.Name);
        }
    }
Damith Asanka
fonte
10

Eu verifiquei o IL em várias respostas. Esse código fornece com eficiência uma lista vazia:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
MEC
fonte
1
Algum motivo para rejeitar minha edição? A resposta a seguir retorna IEnumerable, enquanto minha versão retorna List, exatamente o que o OP pediu.
Necronomicron
Eu prefiro esta abordagem, ou até mesmo mais perto de esta resposta :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim
8

Aqui está a minha tentativa.

List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }}; 

Eu vim com isso quando escrevi algo semelhante para fazer uma Lista Anônima para um tipo personalizado.

user_v
fonte
8

Você pode criar uma lista de dinâmica.

List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
   var anon= new
   {
      Id = model.Id,
      Name=model.Name
   };
   anons.Add(anon);
}

"dinâmico" é inicializado pelo primeiro valor adicionado.

Codinome Jack
fonte
7

Em vez disso:

var o = new { Id = 1, Name = "Foo" }; 
var o1 = new { Id = 2, Name = "Bar" }; 

List <var> list = new List<var>(); 
list.Add(o); 
list.Add(o1);

Você pode fazer isso:

var o = new { Id = 1, Name = "Foo" }; 
var o1 = new { Id = 2, Name = "Bar" }; 

List<object> list = new List<object>(); 
list.Add(o); 
list.Add(o1);

No entanto, você receberá um erro de compilação se tentar fazer algo assim em outro escopo, embora funcione em tempo de execução:

private List<object> GetList()
{ 
    List<object> list = new List<object>();
    var o = new { Id = 1, Name = "Foo" }; 
    var o1 = new { Id = 2, Name = "Bar" }; 
    list.Add(o); 
    list.Add(o1);
    return list;
}

private void WriteList()
{
    foreach (var item in GetList()) 
    { 
        Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine); 
    }
}

O problema é que apenas os membros do Object estão disponíveis no tempo de execução, embora o intellisense mostre o ID e o nome das propriedades .

No .net 4.0, uma solução é usar a palavra-chave dynamic istead of object no código acima.

Outra solução é usar a reflexão para obter as propriedades

using System;
using System.Collections.Generic;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            var anonymous = p.GetList(new[]{
                new { Id = 1, Name = "Foo" },       
                new { Id = 2, Name = "Bar" }
            });

            p.WriteList(anonymous);
        }

        private List<T> GetList<T>(params T[] elements)
        {
            var a = TypeGenerator(elements);
            return a;
        }

        public static List<T> TypeGenerator<T>(T[] at)
        {
            return new List<T>(at);
        }

        private void WriteList<T>(List<T> elements)
        {
            PropertyInfo[] pi = typeof(T).GetProperties();
            foreach (var el in elements)
            {
                foreach (var p in pi)
                {
                    Console.WriteLine("{0}", p.GetValue(el, null));
                }
            }
            Console.ReadLine();
        }
    }
}
Jakob Flygare
fonte
7

Aqui está outro método de criar uma Lista de tipos anônimos que permite iniciar com uma lista vazia, mas ainda ter acesso ao IntelliSense.

var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();

Se você quiser manter o primeiro item, basta colocar uma letra na string.

var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Brackus
fonte
Usando uma corda como um inicializador de matriz pode funcionar, mas é muito má prática
miket
Eu diria que a maioria das respostas acima não é uma prática particularmente "boa", mas isso foi dado devido à natureza da pergunta. Tipos anônimos não foram realmente projetados para funcionar dessa maneira. Estou curioso, porém, por que meu método é "pior" que os outros? Algo que estou perdendo?
Brackus
você está certo, pois a pergunta está pedindo algo que seja uma má prática, não pode haver uma resposta de boa prática, mas como você está usando uma string para gerar uma matriz de caracteres e depois convertendo-a em uma matriz do que o usuário desejar , que são tipos não relacionados para não mencionar a geração de boxe supérfluo e unboxing que é ineficiente
miket
5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
morlock
fonte
5

Esta é uma pergunta antiga, mas pensei em colocar minha resposta em C # 6. Costumo configurar dados de teste que são facilmente inseridos no código como uma lista de tuplas. Com algumas funções de extensão, é possível ter este formato compacto e agradável, sem repetir os nomes em cada entrada.

var people= new List<Tuple<int, int, string>>() {
    {1, 11, "Adam"},
    {2, 22, "Bill"},
    {3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });

Isso fornece um IEnumerable - se você deseja adicionar uma lista, basta adicionar ToList ().

A mágica vem da extensão personalizada Adicionar métodos para tuplas, conforme descrito em https://stackoverflow.com/a/27455822/4536527 .

public static class TupleListExtensions    {
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
            T1 item1, T2 item2)       {
        list.Add(Tuple.Create(item1, item2));
    }

    public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
            T1 item1, T2 item2, T3 item3) {
        list.Add(Tuple.Create(item1, item2, item3));
    }

// and so on...

}

A única coisa que eu não gosto é que os tipos são separados dos nomes, mas se você realmente não deseja criar uma nova classe, essa abordagem ainda permitirá que você tenha dados legíveis.

Peter Davidson
fonte
4

Estou muito surpreso que ninguém tenha sugerido inicializadores de coleção. Dessa forma, só é possível adicionar objetos quando a lista é criada, daí o nome, no entanto, parece a melhor maneira de fazê-lo. Não há necessidade de criar uma matriz e convertê-la em uma lista.

var list = new List<dynamic>() 
{ 
    new { Id = 1, Name = "Foo" }, 
    new { Id = 2, Name = "Bar" } 
};

Você sempre pode usar em objectvez de, dynamicmas tentar mantê-lo de uma maneira genérica verdadeira, dynamicfaz mais sentido.

Tom Dee
fonte
3

Você pode fazer assim:

var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };

var array = new[] { o, o1 };
var list = array.ToList();

list.Add(new { Id = 3, Name = "Yeah" });

Parece um pouco "hacky" para mim, mas funciona - se você realmente precisa ter uma lista e não pode simplesmente usar a matriz anônima.

Jermismo
fonte
3

Para o seu segundo exemplo, em que você precisa inicializar uma nova List<T>, uma idéia é criar uma lista anônima e depois limpá-la.

var list = new[] { o, o1 }.ToList();
list.Clear();

//and you can keep adding.
while (....)
{
    ....
    list.Add(new { Id = x, Name = y });
    ....
}

Ou, como método de extensão, deve ser mais fácil:

public static List<T> GetEmptyListOfThisType<T>(this T item)
{
    return new List<T>();
}

//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();

Ou provavelmente ainda mais curto,

var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
nawfal
fonte
2

Se você estiver usando o C # 7 ou superior, poderá usar tipos de tupla em vez de tipos anônimos.

var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
Bassem
fonte
1

Derivando dessa resposta , criei dois métodos que poderiam fazer a tarefa:

    /// <summary>
    /// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
    /// for the needed type inference. This overload is for when you don't have an instance of the anon class
    /// and don't want to make one to make the list.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="definition"></param>
    /// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
    public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
    {
        return new List<T>();
    }
    /// <summary>
    /// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
    /// only used for the needed type inference. This overload is for when you do have an instance of the anon
    /// class and don't want the compiler to waste time making a temp class to define the type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="definition"></param>
    /// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
    public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
    {
        return new List<T>();
    }

Você pode usar os métodos como

var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);

Essa resposta tem uma idéia semelhante, mas eu não a vi até depois de fazer esses métodos.

BrainStorm.exe
fonte
0

Tente com isto:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}
Matteo Gariglio
fonte
Onde está a lista do tipo anônimo?
Micha Wiedenmann
-14
static void Main()
{
    List<int> list = new List<int>();
    list.Add(2);
    list.Add(3);
    list.Add(5);
    list.Add(7);
}
Ravi Saini
fonte
5
Não vejo aulas anônimas aqui.
Andrew Barber