Eine generische Liste anonymer Klassen

416

In C # 3.0 können Sie eine anonyme Klasse mit der folgenden Syntax erstellen

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

Gibt es eine Möglichkeit, diese anonymen Klassen einer generischen Liste hinzuzufügen?

Beispiel:

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

Ein anderes Beispiel:

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

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}
DHornpout
quelle
2
Beachten Sie, dass alle Objekte im Array gleich eingegeben werden müssen. In seltenen new[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
Fällen
1
Anonyme Typen können als temporärer Speicher verwendet werden. In den meisten Fällen erstellen Sie sie in der LINQ-Select-Anweisung mit Select (i => new {i.ID, i.Name}). Dies würde eine IEnumerable des richtigen Typs zurückgeben, wenn Sie Ihre while-Klausel in eine LINQ.Where-Anweisung umdefinieren. Wo Sie die Liste niemals benötigen sollten, und wenn Sie dies tun, können Sie einfach ToList darauf
aufrufen

Antworten:

427

Du könntest es tun:

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

Es gibt viele Möglichkeiten, diese Katze zu häuten, aber im Grunde verwenden sie alle irgendwo Typinferenz - was bedeutet, dass Sie eine generische Methode aufrufen müssen (möglicherweise als Erweiterungsmethode). Ein anderes Beispiel könnte sein:

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

var list = CreateList(o, o1);

Du hast die Idee :)

Jon Skeet
quelle
32
@DHornpout: Das würde ein Array ergeben, keine Liste <T>.
Jon Skeet
23
@DHornpout: Haben Sie "using System.Linq;" oben in Ihrer Datei? ToList ist ein LINQ-Operator.
Jon Skeet
5
Verstanden .. Muss "using System.Linq" einschließen. Vielen Dank.
DHornpout
2
In Visual Studio scheint es eine Inkonsistenz zu sein, dass Intellisense nicht hilfreicher ist, um fehlende Includes von Assemblys mit referenzierten Erweiterungsmethoden (wie referenzierte Typen) aufzudecken.
LOAS
3
Dieser Mann ist überall, hat heute 8 Fragen gesucht, 7 von ihm beantwortet.
Kugan Kumar
109

Hier ist die Antwort.

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
quelle
12
Dutt, dein Code sollte am Ende ohne .ToList () funktionieren.
DHornpout
3
Okay, cool, jetzt brauchen wir ein Beispiel für das Ersetzen der neuen {} Zeilen durch eine select-Anweisung. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
Topwik
@towpse irgendeine Lösung darüber?
Kiquenet
@Dutt, irgendein Beispiel, wenn ich eine Methode (Funktion) verwende, die eine Liste <T> zurückgibt?
Kiquenet
Nun gibt es Verfahren string.Joinund String - Interpolation, so dass keine Notwendigkeit zu verwenden foreachund Format.
Realsonic
60

Es gibt viele Möglichkeiten, dies zu tun, aber einige der Antworten hier erstellen eine Liste mit Müllelementen, für die Sie die Liste löschen müssen.

Wenn Sie nach einer leeren Liste des generischen Typs suchen, verwenden Sie eine Auswahl gegen eine Liste von Tupeln, um die leere Liste zu erstellen. Es werden keine Elemente instanziiert.

Hier ist der Einzeiler, um eine leere Liste zu erstellen:

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

Dann können Sie es mit Ihrem generischen Typ hinzufügen:

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

Alternativ können Sie wie folgt vorgehen, um die leere Liste zu erstellen (ich bevorzuge jedoch das erste Beispiel, da Sie es auch für eine aufgefüllte Sammlung von Tupeln verwenden können):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   
Paul Rouleau
quelle
1
Ich mag diesen Weg sehr. Danke Paul! Es ist immer ein guter Tag, an dem Sie Tupel verwenden können! xD
Brady Liles
Ich mag das. Es ist schön, eine konkrete Erklärung des Objekts zu haben, das ich übergeben werde.
Morvael
Schön, dass ich gerade mit dem Schreiben des Codes fertig bin, der das Löschen meiner
Listenzeit
Danke für die Idee. Ein Vorschlag, Sie können vermeiden, eine Dummy-Liste Enumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe
45

Nicht genau, aber man kann sagen List<object>und die Dinge werden funktionieren. Funktioniert jedoch list[0].Idnicht.

Dies funktioniert zur Laufzeit in C # 4.0 mit einem List<dynamic>, dh Sie erhalten IntelliSense nicht.

Jeff Moser
quelle
Es ist jedoch nicht stark typisiert, da Sie keine Compiler-Intellisense-Unterstützung für die Elemente in der Liste haben.
Joel Coehoorn
31
Ich fürchte, die Leute werden solche Dinge mit Dynamik tun.
Erikkallen
2
Ich habe nicht gesagt , dass es eine großartige Idee ist, aber dass es möglich ist :-) Es könnte notwendig sein, Objekte von Ruby zu speichern.
Jeff Moser
2
In diesen Fällen ist der Quelltyp jedoch dynamisch. Es ist nicht sinnvoll, eine Liste <dynamisch> für anonyme Typen zu verwenden.
Dykam
1
Sehr hilfreich. Insbesondere, wenn die Liste definiert werden muss, bevor anonyme Elemente hinzugefügt werden.
Karlth
24

ich vermute

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

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

wird funktionieren.

Sie könnten es auch so schreiben:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
erikkallen
quelle
Ja, diese Lösung hilft beim Initialisieren des anonymen Arrays. Vielen Dank.
DHornpout
1
Setzen Sie einfach ein kleines <T> nach den Methodennamen.
Martin
21

Normalerweise benutze ich Folgendes: hauptsächlich, weil Sie dann mit einer Liste "beginnen", die leer ist.

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

In letzter Zeit habe ich es stattdessen so geschrieben:

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

Mit der Wiederholungsmethode können Sie außerdem Folgendes tun:

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

..die gibt Ihnen die erste Liste mit dem ersten bereits hinzugefügten Element.

Rostow
quelle
2
Sie müssen nicht mit einer leeren Liste beginnen - Sie können Range (0,1) ausführen und Ihr erstes Objekt in der select-Anweisung zum ersten Objekt machen.
Matthew M.
1
Sie müssen nicht mit einer leeren Liste beginnen. Wenn Sie wissen, was das erste Element ist (wie im Beispiel), sind Sie in Ihrem Kommentar richtig. Oft verwende ich dies jedoch zum Parsen einer Zwischendatei / Datenquelle und greife erst auf das erste echte Element zu, wenn ich es in einem LINQ-Projektionsszenario verwende (und muss daher das Überspringen des ersten Datensatzes nicht berücksichtigen).
Rostow
19

Sie können dies in Ihrem Code tun.

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

In der neuesten Version 4.0 kann Dynamic wie unten verwendet werden

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

Ich habe die IL auf mehrere Antworten überprüft. Dieser Code liefert effizient eine leere Liste:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
MEC
quelle
1
Gibt es einen Grund, meine Bearbeitung abzulehnen? Die folgende Antwort kehrt zurück IEnumerable, während meine Version Listgenau das zurückgibt , was OP gefragt hat.
Necronomicron
Ich bevorzuge diesen Ansatz oder sogar einen, der dieser Antwort näher kommt :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
Palswim
8

Hier ist mein Versuch.

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

Ich kam darauf, als ich etwas Ähnliches schrieb, um eine anonyme Liste für einen benutzerdefinierten Typ zu erstellen.

user_v
quelle
8

Sie können eine dynamische Liste erstellen.

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

"dynamisch" wird durch den ersten Mehrwert initialisiert.

Codename Jack
quelle
7

An Stelle von:

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

Sie könnten dies tun:

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

Sie erhalten jedoch einen Kompilierungsfehler, wenn Sie versuchen, so etwas in einem anderen Bereich auszuführen, obwohl dies zur Laufzeit funktioniert:

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

Das Problem ist, dass zur Laufzeit nur die Mitglieder von Object verfügbar sind, obwohl Intellisense die Eigenschaften- ID und den Namen anzeigt .

In .net 4.0 besteht eine Lösung darin, das Schlüsselwort dynamic istead of object im obigen Code zu verwenden.

Eine andere Lösung besteht darin, Reflexion zu verwenden, um die Eigenschaften zu erhalten

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
quelle
7

Hier ist eine weitere Methode zum Erstellen einer Liste anonymer Typen, mit der Sie mit einer leeren Liste beginnen können, aber dennoch Zugriff auf IntelliSense haben.

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

Wenn Sie das erste Element behalten möchten, geben Sie einfach einen Buchstaben in die Zeichenfolge ein.

var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Brackus
quelle
Die Verwendung eines Strings als Array-Initialisierer mag funktionieren, ist aber eine sehr schlechte Praxis
MikeT
Ich würde sagen, dass die meisten der oben genannten Antworten keine besonders "gute" Praxis sind, aber das war aufgrund der Art der Frage eine Selbstverständlichkeit. Anonyme Typen waren nicht wirklich dafür ausgelegt, auf diese Weise zu funktionieren. Ich bin allerdings neugierig, warum meine Methode "schlechter" ist als die anderen? Fehlt mir etwas?
Brackus
Sie haben Recht, da die Frage nach etwas fragt, das selbst eine schlechte Übung ist. Es gibt keine Antwort auf eine gute Übung, aber weil Sie eine Zeichenfolge verwenden, um ein Array von Zeichen zu generieren und diese dann in ein Array von allem umzuwandeln, was der Benutzer will , die nicht verwandte Typen sind, ganz zu schweigen davon, überflüssiges Boxen und Unboxing zu erzeugen, was ineffizient ist
MikeT
5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
Morlock
quelle
5

Dies ist eine alte Frage, aber ich dachte, ich würde meine C # 6-Antwort eingeben. Ich muss oft Testdaten einrichten, die einfach als Liste von Tupeln in den Code eingegeben werden können. Mit ein paar Erweiterungsfunktionen ist es möglich, dieses schöne, kompakte Format zu haben, ohne die Namen bei jedem Eintrag zu wiederholen.

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

Dies ergibt eine IEnumerable - wenn Sie eine Liste möchten, die Sie hinzufügen können, fügen Sie einfach ToList () hinzu.

Die Magie kommt von der benutzerdefinierten Erweiterung Add-Methoden für Tupel, wie unter https://stackoverflow.com/a/27455822/4536527 beschrieben .

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...

}}

Das einzige, was mir nicht gefällt, ist, dass die Typen von den Namen getrennt sind. Wenn Sie jedoch wirklich keine neue Klasse erstellen möchten, können Sie mit diesem Ansatz trotzdem lesbare Daten erhalten.

Peter Davidson
quelle
4

Ich bin sehr überrascht, dass niemand Sammlungsinitialisierer vorgeschlagen hat. Auf diese Weise können nur Objekte hinzugefügt werden, wenn die Liste erstellt wird, daher der Name. Dies scheint jedoch die beste Methode zu sein. Sie müssen kein Array erstellen und dann in eine Liste konvertieren.

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

Sie können immer verwenden, objectanstatt zu dynamicversuchen, es auf eine wirklich generische Weise zu halten, dann dynamicist es sinnvoller.

Tom Dee
quelle
3

Sie können es so machen:

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

Es scheint mir ein wenig "hacky" zu sein, aber es funktioniert - wenn Sie wirklich eine Liste benötigen und nicht einfach das anonyme Array verwenden können.

Jermismo
quelle
3

In Ihrem zweiten Beispiel, in dem Sie eine neue initialisieren müssen List<T>, besteht eine Idee darin, eine anonyme Liste zu erstellen und diese dann zu löschen.

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

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

Oder als Erweiterungsmethode sollte es einfacher sein:

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

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

Oder wahrscheinlich noch kürzer,

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

Aus dieser Antwort habe ich zwei Methoden entwickelt, mit denen die Aufgabe erledigt werden kann:

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

Sie können die Methoden wie verwenden

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

Diese Antwort hat eine ähnliche Idee, aber ich habe sie erst gesehen, nachdem ich diese Methoden gemacht habe.

BrainStorm.exe
quelle
1

Wenn Sie C # 7 oder höher verwenden, können Sie Tupeltypen anstelle von anonymen Typen verwenden.

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

Versuchen Sie es damit:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}
Matteo Gariglio
quelle
Wo ist die Liste der anonymen Typen?
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
quelle
5
Ich sehe hier keine anonymen Klassen.
Andrew Barber