Un elenco generico di classe anonima


416

In C # 3.0 puoi creare una classe anonima con la seguente sintassi

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

C'è un modo per aggiungere queste classi anonime a un elenco generico?

Esempio:

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

Un altro esempio:

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

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}

2
Si noti che tutti gli oggetti devono essere digitati nello stesso modo nell'array. Raramente potresti aver bisogno di aiuto con un cast, specialmente per i nullnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS,

1
i tipi anonimi sono progettati per essere utilizzati come memoria temporanea, nella maggior parte dei casi li creeresti nell'istruzione LINQ select usando Select (i => new {i.ID, i.Name}); che restituirebbe un IEnumerable del tipo corretto se ridefinissi la tua clausola while in un LINQ. Dove un'istruzione non dovresti mai avere bisogno dell'elenco e se lo facessi puoi semplicemente chiamare ToList su di esso
MikeT

Risposte:


428

Potresti fare:

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

Esistono molti modi per scuoiare questo gatto, ma fondamentalmente useranno tutti l'inferenza del tipo da qualche parte - il che significa che devi chiamare un metodo generico (possibilmente come metodo di estensione). Un altro esempio potrebbe essere:

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

var list = CreateList(o, o1);

Ti viene l'idea :)


32
@DHornpout: questo darebbe un array, non un Elenco <T>.
Jon Skeet,

23
@DHornpout: hai "using System.Linq;" nella parte superiore del tuo file? ToList è un operatore LINQ.
Jon Skeet,

5
Capito .. È necessario includere "using System.Linq". Grazie.
DHornpout,

2
Sembra incoerenza in Visual Studio, che intellisense non sia più utile per scoprire inclusioni mancanti di assiemi con metodi di estensione referenziati (come i tipi referenziati).
LOAS

3
quest'uomo è ovunque, ha cercato 8 domande oggi, 7 risposte da lui.
Kugan Kumar,

109

Ecco la risposta

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

12
Dutt, il tuo codice dovrebbe funzionare senza .ToList () alla fine.
DHornpout,

3
va bene, ora abbiamo bisogno di un esempio di sostituzione delle nuove {} righe con un'istruzione select. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik

@towpse qualche soluzione al riguardo?
Kiquenet,

@Dutt, qualche esempio se uso un metodo (funzione) che restituisce un Elenco <T>?
Kiquenet,

Ora ci sono metodi string.Joine interpolazioni di stringhe, quindi non c'è bisogno di usare foreache Format.
realsonic,

61

Esistono molti modi per farlo, ma alcune delle risposte qui stanno creando un elenco che contiene elementi di immondizia, che richiede di cancellare l'elenco.

Se stai cercando un elenco vuoto di tipo generico, usa un Seleziona contro un elenco di tuple per creare un elenco vuoto. Nessun elemento verrà istanziato.

Ecco il one-liner per creare un elenco vuoto:

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

Quindi puoi aggiungervi usando il tuo tipo generico:

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

In alternativa, puoi fare qualcosa come sotto per creare un elenco vuoto (Ma preferisco il primo esempio perché puoi usarlo anche per una raccolta popolata di Tuple):

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

1
Mi piace così tanto. Grazie Paolo! È sempre una buona giornata in cui puoi usare Tuple! xD
Brady Liles il

Mi piace questo. È bello avere una sorta di dichiarazione concreta dell'oggetto che sto per passare.
Morvael

Nizza Ho appena finito il codice di scrittura che ha incluso la compensazione mio tempo lista di riscriverlo
JoshBerke

Grazie per l'idea Un suggerimento, è possibile evitare di allocare un elenco fittizio se si utilizzaEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe

45

Non esattamente, ma puoi dire List<object>e le cose funzioneranno. Tuttavia, list[0].Idnon funzionerà.

Ciò funzionerà a runtime in C # 4.0 avendo un List<dynamic>, cioè non sarà possibile ottenere IntelliSense.


Non è fortemente tipizzato, tuttavia, nel senso che non avrai il supporto del compilatore intellisense per gli elementi nell'elenco.
Joel Coehoorn,

31
Questo è il tipo di cose che temo che le persone faranno con la dinamica.
erikkallen,

2
Non ho detto che è stata una grande idea, ma che è stato possibile :-) Potrebbe esserci la necessità, ad esempio, di conservare oggetti da Ruby.
Jeff Moser,

2
Ma in quei casi, dopo tutto, il tipo di sorgente è dinamico, non ha senso usare un Elenco <dinamico> per tipi anonimi.
Dykam,

1
Molto utile. Soprattutto se l'elenco deve essere definito prima che vengano aggiunti elementi anonimi.
Karlth,

24

suppongo

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

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

funzionerà.

Potresti anche considerare di scriverlo in questo modo:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}

Sì, questa soluzione aiuterà a risolvere l'inizializzazione dell'array anonimo. Grazie.
DHornpout,

1
Metti solo un po '<T> dopo il nome del metodo.
Martin,

21

Di solito uso quanto segue; principalmente perché poi "inizi" con un elenco vuoto.

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

Ultimamente, l'ho scritto in questo modo invece:

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

L'uso del metodo di ripetizione ti consentirebbe anche di fare:

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

..che ti dà la lista iniziale con il primo elemento già aggiunto.


2
Non è necessario iniziare con un elenco vuoto: puoi fare Range (0,1) e rendere il tuo primo oggetto nell'istruzione select essere .. il primo oggetto.
Matthew M.,

1
Non è necessario iniziare con un elenco vuoto: nel caso in cui tu sappia qual è il primo elemento (come nell'esempio), allora hai ragione nel tuo commento. Molte volte però lo uso per analizzare un file / origine dati intermedio e non accedo al primo vero oggetto fino a quando non lo utilizzo in uno scenario di proiezione LINQ (e quindi non è necessario tenere conto del salto del primo record).
Rostov,

19

Puoi farlo nel tuo codice.

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

11

Nell'ultima versione 4.0, è possibile utilizzare la dinamica come di seguito

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

10

Ho controllato l'IL su diverse risposte. Questo codice fornisce in modo efficiente un elenco vuoto:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();

1
Qualche motivo per rifiutare la mia modifica? La seguente risposta ritorna IEnumerable, mentre la mia versione ritorna List, esattamente ciò che OP ha chiesto.
Necronomicron,

Preferisco questo approccio o anche uno più vicino a questa risposta :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim,

8

Ecco il mio tentativo.

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

Ho pensato a questo quando ho scritto qualcosa di simile per creare un elenco anonimo per un tipo personalizzato.


8

È possibile creare un elenco di dinamici.

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

"dinamico" viene inizializzato dal primo valore aggiunto.


7

Invece di questo:

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

Potresti farlo:

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

Tuttavia, otterrai un errore compiletime se provi a fare qualcosa del genere in un altro ambito, anche se funziona in fase di esecuzione:

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

Il problema è che solo i membri di Object sono disponibili in fase di esecuzione, sebbene intellisense mostrerà le proprietà id e name .

In .net 4.0 una soluzione consiste nell'utilizzare la parola chiave dynamic istead di oggetto nel codice sopra.

Un'altra soluzione è utilizzare la riflessione per ottenere le proprietà

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

7

Ecco un altro metodo per creare un elenco di tipi anonimi che ti consente di iniziare con un elenco vuoto, ma hai ancora accesso a IntelliSense.

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

Se si desidera mantenere il primo elemento, basta inserire una lettera nella stringa.

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

L'uso di una stringa come inizializzatore di array può funzionare ma è una pessima pratica
MikeT,

Direi che la maggior parte delle risposte di cui sopra non sono particolarmente "buone" pratiche, ma è stata una specie di dato dato dalla natura della domanda. I tipi anonimi non sono stati progettati per funzionare in questo modo. Sono curioso però perché il mio metodo sia "peggiore" rispetto agli altri? Qualcosa che mi manca?
Brackus,

hai ragione poiché la domanda è chiedere qualcosa che di per sé cattiva pratica non può esserci una risposta di buona pratica, ma perché stai usando una stringa per generare una matrice di caratteri e poi convertendola in una matrice di ciò che l'utente desidera , che sono tipi non correlati per non parlare della generazione di boxe superflue e unboxing che è inefficiente
MikeT

5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);

5

Questa è una vecchia domanda, ma ho pensato di inserire la mia risposta C # 6. Spesso devo impostare i dati di test che possono essere facilmente inseriti nel codice come un elenco di tuple. Con un paio di funzioni di estensione, è possibile avere questo formato carino e compatto, senza ripetere i nomi su ogni voce.

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

Questo dà un IEnumerable - se vuoi un elenco che puoi aggiungere, allora aggiungi ToList ().

La magia deriva dall'estensione personalizzata Aggiungi metodi per le tuple, come descritto su 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...

}

L'unica cosa che non mi piace è che i tipi sono separati dai nomi, ma se davvero non vuoi creare una nuova classe, questo approccio ti consentirà comunque di avere dati leggibili.


4

Sono molto sorpreso che nessuno abbia suggerito inizializzatori di raccolta. In questo modo è possibile aggiungere oggetti solo quando viene creato l'elenco, quindi il nome, tuttavia, sembra il modo migliore per farlo. Non è necessario creare un array, quindi convertirlo in un elenco.

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

Puoi sempre usare objectinvece di, dynamicma provare a mantenerlo in un vero modo generico quindi dynamicha più senso.


3

Puoi farlo in questo modo:

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

Mi sembra un po '"confuso", ma funziona - se hai davvero bisogno di un elenco e non puoi semplicemente usare l'array anonimo.


3

Per il tuo secondo esempio, in cui devi inizializzare un nuovo List<T>, un'idea è quella di creare un elenco anonimo e quindi cancellarlo.

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

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

O come metodo di estensione, dovrebbe essere più semplice:

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

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

O probabilmente anche più breve,

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

2

Se si utilizza C # 7 o versioni successive, è possibile utilizzare tipi di tupla anziché tipi anonimi.

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

1

Derivando da questa risposta , ho escogitato due metodi che potevano svolgere il compito:

    /// <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>();
    }

Puoi usare metodi come

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

Questa risposta ha un'idea simile, ma non l'ho vista fino a dopo aver fatto quei metodi.


0

Prova con questo:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

Dov'è l'elenco di tipo anonimo?
Micha Wiedenmann

-14
static void Main()
{
    List<int> list = new List<int>();
    list.Add(2);
    list.Add(3);
    list.Add(5);
    list.Add(7);
}

5
Non vedo lezioni anonime, qui.
Andrew Barber,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.