Ogólna lista anonimowych klas

416

W C # 3.0 możesz utworzyć anonimową klasę o następującej składni

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

Czy istnieje sposób na dodanie tych anonimowych klas do ogólnej listy?

Przykład:

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

Inny przykład:

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

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}
DHornpout
źródło
2
Zauważ, że wszystkie obiekty muszą być wpisane tak samo w tablicy. Rzadko możesz potrzebować pomocy z obsadą, szczególnie dla new[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
zer.
1
typy anonimowe są przeznaczone do użycia jako tymczasowe miejsce przechowywania, w większości przypadków należy je utworzyć w instrukcji LINQ select za pomocą Select (i => new {i.ID, i.Name}); która zwróciłaby IEnumerable poprawnego typu, jeśli przedefiniujesz swoją klauzulę while w LINQ. Gdzie instrukcja nie powinna nigdy potrzebować listy, a jeśli tak, możesz po prostu wywołać na niej ToList
MikeT

Odpowiedzi:

427

Mógłbyś:

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

Jest wiele sposobów na skórowanie tego kota, ale w zasadzie wszyscy używają gdzieś wnioskowania typu - co oznacza, że ​​musisz wywoływać metodę ogólną (być może jako metodę rozszerzenia). Innym przykładem może być:

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

var list = CreateList(o, o1);

Masz pomysł :)

Jon Skeet
źródło
32
@DHornpout: To dałoby tablicę, a nie Listę <T>.
Jon Skeet
23
@DHornpout: Czy masz „za pomocą System.Linq;” na górze pliku? ToList jest operatorem LINQ.
Jon Skeet
5
Rozumiem. Musisz dołączyć „using System.Linq”. Dzięki.
DHornpout,
2
W Visual Studio wydaje się niespójność, że inteligencja nie jest bardziej pomocna w wykrywaniu brakujących zestawów zestawów z referencyjnymi metodami rozszerzenia (takimi samymi jak typy referencyjne).
LOAS
3
ten człowiek jest wszędzie, przeszukał dziś 8 pytań, na które odpowiedział 7.
Kugan Kumar
109

Oto odpowiedź.

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
źródło
12
Dutt, twój kod powinien działać bez końcówki .ToList ().
DHornpout,
3
okej spoko, teraz potrzebujemy przykładu zastąpienia nowych wierszy {} instrukcją select. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik 24.01.2013
@towpse jakieś rozwiązanie na ten temat?
Kiquenet,
@Dutt, jakakolwiek próbka, jeśli użyję metody (funkcji), która zwraca List <T>?
Kiquenet,
Teraz są metody string.Joini interpolacja ciągów, więc nie trzeba używać foreachi Format.
realsonic
61

Można to zrobić na wiele sposobów, ale niektóre odpowiedzi tutaj tworzą listę zawierającą elementy śmieci, co wymaga wyczyszczenia listy.

Jeśli szukasz pustej listy typu ogólnego, użyj opcji Wybierz przeciwko liście krotek, aby utworzyć pustą listę. Żadne elementy nie zostaną utworzone.

Oto jeden wiersz, aby utworzyć pustą listę:

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

Następnie możesz dodać do niego swój typ ogólny:

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

Alternatywnie możesz zrobić coś takiego jak poniżej, aby utworzyć pustą listę (ale wolę pierwszy przykład, ponieważ możesz go użyć również do zapełnionej kolekcji Tuples):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   
Paul Rouleau
źródło
1
Bardzo mi się to podoba. Dzięki Paul! Zawsze jest dobry dzień, kiedy możesz użyć Tuples! xD
Brady Liles
Lubię to. Miło jest mieć jakąś konkretną deklarację obiektu, który zamierzam przekazać.
Morvael
Fajnie, właśnie skończyłem pisać kod, który obejmował czyszczenie mojej listy, aby ją przepisać
JoshBerke
Dzięki za pomysł. Sugeruje się, że można uniknąć przydzielania listy fikcyjnej, jeśli używaszEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe
45

Nie do końca, ale można powiedzieć, że List<object>wszystko będzie działać. Jednak list[0].Idnie zadziała.

Będzie to działać w środowisku wykonawczym w C # 4.0 List<dynamic>, ponieważ oznacza to, że nie uzyskasz IntelliSense.

Jeff Moser
źródło
Nie jest to jednak mocno wpisane, w tym sensie, że nie będzie obsługiwać inteligencji kompilatora dla elementów na liście.
Joel Coehoorn
31
Tego rodzaju rzeczy obawiam się, że ludzie zrobią z dynamiką.
erikkallen
2
Nie powiedziałem, że to świetny pomysł, ale że jest to możliwe :-) Może zaistnieć potrzeba przechowywania na przykład przedmiotów z Ruby.
Jeff Moser
2
Ale w takich przypadkach typ źródła jest dynamiczny, nie ma sensu używać Listy <dynamicznej> dla typów anonimowych.
Dykam
1
Bardzo pomocny. Zwłaszcza jeśli lista musi zostać zdefiniowana przed dodaniem do niej anonimowych elementów.
Karlth,
24

zgaduję

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

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

będzie działać.

Możesz również rozważyć napisanie tego w ten sposób:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
erikkallen
źródło
Tak, to rozwiązanie pomoże rozwiązać inicjalizację tablicy Anonimowy. Dzięki.
DHornpout,
1
Po prostu wstaw trochę <T> po nazwie metody.
Martin
21

Zwykle używam następujących; głównie dlatego, że „zaczynasz” z pustą listą.

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

Ostatnio pisałem to w ten sposób:

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

Zastosowanie metody powtarzania pozwoliłoby również na:

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

... która daje początkową listę z już dodanym pierwszym elementem.

Rostów
źródło
2
Nie musisz zaczynać od pustej listy - możesz zrobić Range (0,1) i ustawić swój pierwszy obiekt w instrukcji select jako… pierwszy obiekt.
Matthew M.
1
Nie musisz zaczynać od pustej listy - w przypadku, gdy wiesz, jaki jest pierwszy element (jak w przykładzie), masz rację w swoim komentarzu. Jednak wiele razy używam tego do analizowania pośredniego pliku / źródła danych i nie uzyskuję dostępu do pierwszego prawdziwego elementu, dopóki nie użyję go w scenariuszu projekcji LINQ (a zatem nie muszę brać pod uwagę pominięcia pierwszego rekordu).
Rostów
19

Możesz to zrobić w swoim kodzie.

var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
Malachit BR
źródło
11

W najnowszej wersji 4.0 można używać dynamiki jak poniżej

var list = new List<dynamic>();
        list.Add(new {
            Name = "Damith"
    });
        foreach(var item in list){
            Console.WriteLine(item.Name);
        }
    }
Damith Asanka
źródło
10

Sprawdziłem IL na kilka odpowiedzi. Ten kod skutecznie zapewnia pustą listę:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
MEC
źródło
1
Czy jest jakiś powód do odrzucenia mojej edycji? Po odpowiedzi wraca IEnumerable, podczas gdy moja wersja powraca List, dokładnie o co prosi OP.
Necronomicron
Wolę takie podejście, a nawet bliższe tej odpowiedzi :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim
8

Oto moja próba.

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

Wymyśliłem to, kiedy napisałem coś podobnego do stworzenia Anonimowej listy dla niestandardowego typu.

użytkownik_v
źródło
8

Możesz utworzyć listę dynamiczną.

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

„dynamiczny” jest inicjalizowany przez pierwszą wartość dodaną.

Nazwa kodowa Jack
źródło
7

Zamiast tego:

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

Możesz to zrobić:

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

Jeśli jednak spróbujesz zrobić coś takiego w innym zakresie, pojawi się błąd czasu kompilacji, chociaż działa on w czasie wykonywania:

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

Problem polega na tym, że tylko członkowie Object są dostępni w czasie wykonywania, chociaż intellisense pokaże identyfikator właściwości i nazwę .

W .net 4.0 rozwiązaniem jest użycie słowa kluczowego dynamiczna istead obiektu w powyższym kodzie.

Innym rozwiązaniem jest użycie odbicia, aby uzyskać właściwości

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
źródło
7

Oto kolejna metoda tworzenia Listy anonimowych typów, która pozwala zacząć od pustej listy, ale nadal mieć dostęp do IntelliSense.

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

Jeśli chcesz zachować pierwszy element, po prostu umieść jedną literę w ciągu.

var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Brackus
źródło
Używanie łańcucha jako inicjalizatora tablicy może działać, ale jest to bardzo zła praktyka
MikeT
Powiedziałbym, że większość powyższych odpowiedzi nie jest szczególnie „dobrą” praktyką, ale było to coś w rodzaju danych ze względu na charakter pytania. Anonimowe typy nie zostały tak zaprojektowane, aby działały w ten sposób. Zastanawiam się jednak, dlaczego moja metoda jest „gorsza” od innych? Czegoś mi brakuje?
Brackus,
masz rację, ponieważ pytanie wymaga czegoś, co samo w sobie jest złą praktyką, nie może być odpowiedzi na dobrą praktykę, ale ponieważ używasz ciągu znaków, aby wygenerować tablicę znaków, a następnie przekształcić ją w tablicę wszystkiego, czego chce użytkownik , które są niepowiązanymi typami, nie mówiąc już o generowaniu zbędnego boksu i rozpakowaniu, który jest nieefektywny
MikeT
5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
morlock
źródło
5

To stare pytanie, ale pomyślałem, że wstawię odpowiedź C # 6. Często muszę skonfigurować dane testowe, które można łatwo wprowadzić w kodzie jako listę krotek. Dzięki kilku funkcjom rozszerzenia można mieć ten ładny, kompaktowy format, bez powtarzania nazw dla każdego wpisu.

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

To daje IEnumerable - jeśli chcesz listę, którą możesz dodać, po prostu dodaj ToList ().

Magia pochodzi z niestandardowego rozszerzenia Dodaj metody krotek, jak opisano na 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...

}

Jedyne, co mi się nie podoba, to to, że typy są oddzielone od nazw, ale jeśli naprawdę nie chcesz tworzyć nowej klasy, to podejście pozwoli ci mieć czytelne dane.

Peter Davidson
źródło
4

Jestem bardzo zaskoczony, że nikt nie sugerował inicjalizacji kolekcji. W ten sposób można dodawać obiekty tylko podczas tworzenia listy, stąd nazwa, jednak wydaje się, że jest to najładniejszy sposób. Nie trzeba tworzyć tablicy, a następnie konwertować jej na listę.

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

Zawsze możesz użyć objectzamiast, dynamicale starając się zachować to w sposób ogólny, wtedy dynamicma to większy sens.

Tom Dee
źródło
3

Możesz to zrobić w ten sposób:

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

Wydaje mi się to trochę „hacky”, ale działa - jeśli naprawdę potrzebujesz listy i nie możesz po prostu użyć anonimowej tablicy.

Jermismo
źródło
3

W drugim przykładzie, w którym musisz zainicjować nowy List<T>, jednym z pomysłów jest utworzenie anonimowej listy, a następnie wyczyszczenie jej.

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

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

Lub jako metoda rozszerzenia powinna być łatwiejsza:

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

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

A może nawet krótszy,

var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
nawfal
źródło
2

Jeśli używasz C # 7 lub nowszego, możesz użyć typów krotek zamiast typów anonimowych.

var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
Bassem
źródło
1

Na podstawie tej odpowiedzi opracowałem dwie metody, które mogłyby wykonać to zadanie:

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

Możesz użyć metod takich jak

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

Ta odpowiedź ma podobny pomysł, ale nie widziałam jej, dopóki nie stworzyłem tych metod.

BrainStorm.exe
źródło
0

Spróbuj tego:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}
Matteo Gariglio
źródło
Gdzie jest lista typu anonimowego?
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
źródło
5
Nie widzę tu anonimowych zajęć.
Andrew Barber,