2009-03-04 32 views
329

In C# 3.0 è possibile creare classe anonima con la seguente sintassiUn elenco generico di classe anonima

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

Esiste un modo per aggiungere questi classe anonima ad 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}); 
    .... 
} 
+1

Si noti che tutti gli oggetti devono essere digitati nello stesso array. Raramente potresti aver bisogno di aiuto con un cast, specialmente per null 'new [] {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 in LINQ select statement usando Select (i => new {i.ID, i.Name}); che restituirebbe un IEnumerable del tipo corretto se ridefinisci la tua clausola while in una LINQ.Quando non dovresti mai aver bisogno dell'elenco e se lo hai fatto puoi semplicemente chiamare ToList su di esso – MikeT

risposta

349

Si potrebbe fare:

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

Ci sono molti modi di scuoiare questo gatto, ma in fondo saranno tutti di tipo utilizzo deduzione da qualche parte - il che significa che hai avuto modo di essere chiamata 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); 

si ottiene l'idea :)

+0

La sintassi corretta (almeno nella 3.5) è var list = new [] {o, o1}; – DHornpout

+27

@DHornpout: ciò fornirebbe un array, non un elenco . –

+1

Quale versione stai usando? Questo è l'errore del compilatore che ho ottenuto Errore 'System.Array' non contiene una definizione per 'ToList' e non è possibile trovare il metodo di estensione 'ToList' che accetta un primo argomento di tipo 'System.Array' (sono manca una direttiva using o un riferimento all'assembly?) – DHornpout

37

Non esattamente, ma si può dire che List<object> e le cose funzioneranno. Tuttavia, list[0].Id non funzionerà.

Ciò funzionerà in fase di esecuzione in C# 4.0 avendo un List<dynamic>, ovvero non si otterrà IntelliSense.

+0

Non è fortemente tipizzato, però, nel senso che non avrai il supporto intellisense del compilatore per gli elementi nella lista. –

+22

Questo è il tipo di cose che temo che le persone facciano con la dinamica. – erikkallen

+1

Non ho detto che era una * ottima * idea, ma che era possibile :-) Ci potrebbe essere una necessità se si memorizzano oggetti da Ruby per esempio. –

21

Credo

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

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

funzionerà.

Si potrebbe anche non crei in questo modo:

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

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

+0

Basta inserire un po 'di dopo il nome del metodo. – Martin

2

si può fare 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" }); 

sembra un po ' "hacky" per me, ma funziona - se davvero bisogno di avere una lista e non può semplicemente usare la matrice anonima.

84

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

+2

ok, ora abbiamo bisogno di un esempio di sostituzione delle nuove {} righe con un'istruzione select. Lista var = sourceList.Select ( o => nuova { o.ModelId, o.PartNumber, o.Quantity }) ToList().; – topwik

+0

@towpse qualche soluzione al riguardo? – Kiquenet

4

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

Si potrebbe fare questo:

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, si otterrà un errore di compiletime se si tenta di fare qualcosa di simile in un altro campo di applicazione, anche se funziona in runtime:

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 runtime, sebbene intellisense mostrerà le proprietà id e nome.

In .net 4.0 una soluzione è utilizzare la parola chiave dinamica nell'oggetto oggetto nel codice sopra.

Un'altra soluzione è quella di utilizzare la reflection 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

Qui è il mio tentativo.

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

L'ho trovato quando ho scritto qualcosa di simile per creare un elenco anonimo per un tipo personalizzato.

5
var list = new[]{ 
new{ 
FirstField = default(string), 
SecondField = default(int), 
ThirdField = default(double) 
} 
}.ToList(); 
list.RemoveAt(0); 
-11
static void Main() 
{ 
    List<int> list = new List<int>(); 
    list.Add(2); 
    list.Add(3); 
    list.Add(5); 
    list.Add(7); 
} 
+4

Non vedo classi anonime, qui. –

17

È possibile farlo nel codice.

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

Per il vostro secondo esempio, dove si deve inizializzare una nuova 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 un metodo di estensione, dovrebbe essere più facile:

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 ancora più breve,

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

Io di solito uso il seguente; principalmente perché tu "inizi" con una lista vuota.

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

Ultimamente, ho scritto in questo modo invece:

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

Utilizzando il metodo di ripetizione 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 te la dà elenco iniziale con il primo elemento già aggiunto.

+2

Non è necessario iniziare con una lista vuota - è possibile eseguire Range (0,1) e rendere il primo oggetto nell'istruzione select essere .. il primo oggetto. –

+1

Non è necessario iniziare con una lista vuota - nel caso in cui si sappia quale sia il primo elemento (come nell'esempio), allora hai ragione nel tuo commento. Molte volte però lo uso per analizzare un file intermedio/origine dati e non accedere al primo oggetto vero fino a quando non lo utilizzo in uno scenario di proiezione LINQ (e quindi non è necessario considerare il salto del primo record). – Rostov

0

prova con questo:

var result = new List<object>(); 

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

Dov'è l'elenco di tipo anonimo? –

6

Ecco un altro metodo di creazione di un elenco di tipi anonimi che permette di iniziare con una lista vuota, ma hanno ancora accesso a IntelliSense.

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

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

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

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

+0

Direi che la maggior parte delle risposte di cui sopra non sono particolarmente "buone", ma era una sorta di dato dovuto alla natura della domanda. I tipi anonimi non erano realmente progettati per funzionare in questo modo. Sono curioso però perché il mio metodo è "peggiore" rispetto agli altri? Qualcosa che mi manca? – Brackus

+0

hai ragione perché la domanda è chiedendo qualcosa che di per sé pratica sbagliata non può essere una risposta di buona pratica, ma perché stai usando una stringa per generare una matrice di caratteri e quindi convertirla in una serie di ciò che mai l'utente vuole, che sono tipi non correlati per non parlare della generazione di boxe superflui e di unboxing che è inefficiente – MikeT

25

Ci sono molti modi per fare questo, ma alcune delle risposte qui stanno creando un elenco che contiene elementi della spazzatura, che richiede di cancellare l'elenco.

Se si sta cercando un elenco vuoto del tipo generico, utilizzare un Seleziona contro un elenco di tuple per creare l'elenco vuoto. Nessun elemento verrà istanziato.

Ecco l'one-liner per creare una lista vuota:

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

Quindi è possibile aggiungere ad esso utilizzando il vostro tipo generico:

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

In alternativa, si può fare qualcosa di simile qui sotto per creare la lista vuota (Ma preferisco il primo esempio perché puoi usarlo anche per una collezione popolata di Tuples):

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

Mi piace così tanto. Grazie Paolo! È sempre una buona giornata quando puoi usare Tuples! xD –

+0

Mi piace questo. È bello avere una sorta di dichiarazione concreta dell'oggetto che ho intenzione di passare. – Morvael

+0

Nice Ho appena finito di scrivere il codice che includeva la cancellazione del mio elenco per riscriverlo – JoshBerke

7

Ho controllato l'IL su diverse risposte. Questo codice fornisce in modo efficiente una lista vuota:

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 risposta seguente restituisce 'IEnumerable', mentre la mia versione restituisce' List', esattamente ciò che l'OP ha chiesto. – Necronomicron

+0

Preferisco questo approccio, o anche uno più vicino a [questa risposta] (http://stackoverflow.com/a/15749796/393280): 'nuovo oggetto [] {} .Seleziona (o => nuovo {Id = predefinito (int), Name = default (stringa)}). ToList() ' – palswim

+0

Questo è un piccolo trucco. Mi piace molto questo approccio. – Hinrich

3

Questa è una domanda vecchia, ma ho pensato di inserire la risposta C# 6. Spesso devo impostare i dati di test che sono facilmente inseriti in codice come elenco di tuple. Con un paio di funzioni di estensione, è possibile avere questo formato piacevole e compatto, senza ripetere i nomi su ciascuna 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 si desidera un elenco che è possibile aggiungere a quel momento basta aggiungere ToList().

La magia viene dall'estensione personalizzata Aggiungi metodi per le tuple, come descritto a 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 si vuole fare una nuova classe, allora questo approccio sarà ancora permetterà di avere dati leggibili

3

Nella versione 4.0, può usare dinamico come di seguito

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

È possibile creare un elenco di dinamica.

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.