Daftar umum kelas anonim

416

Di C # 3.0 Anda dapat membuat kelas anonim dengan sintaks berikut

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

Apakah ada cara untuk menambahkan kelas anonim ini ke daftar generik?

Contoh:

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

Contoh lain:

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

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}
Dornpout
sumber
2
Perhatikan bahwa semua objek harus diketik sama dalam array. Jarang, Anda mungkin perlu membantu pemain, terutama untuk nullsnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS
1
tipe anonim dirancang untuk digunakan sebagai penyimpanan sementara, dalam kebanyakan kasus Anda akan membuatnya dalam pernyataan pilih LINQ menggunakan Select (i => new {i.ID, i.Name}); yang akan mengembalikan IEnumerable dari jenis yang benar jika Anda mendefinisikan kembali klausa sementara Anda ke dalam LINQ. Di mana pernyataan Anda tidak perlu memerlukan daftar dan jika Anda melakukannya, Anda dapat memanggil ToList di atasnya
MikeT

Jawaban:

428

Anda bisa melakukannya:

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

Ada banyak cara menguliti kucing ini, tetapi pada dasarnya mereka semua akan menggunakan jenis inferensi di suatu tempat - yang berarti Anda harus memanggil metode generik (mungkin sebagai metode ekstensi). Contoh lain mungkin:

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

var list = CreateList(o, o1);

Anda mendapatkan idenya :)

Jon Skeet
sumber
32
@DHornpout: Itu akan memberikan array, bukan Daftar <T>.
Jon Skeet
23
@DHornpout: Apakah Anda memiliki "menggunakan System.Linq;" di bagian atas file Anda? ToList adalah operator LINQ.
Jon Skeet
5
Mengerti .. Perlu memasukkan "using System.Linq". Terima kasih.
DHornpout
2
Tampaknya memang tidak konsisten dalam Visual Studio, bahwa Intellisense tidak lebih membantu dalam mengungkap hilang termasuk majelis dengan metode ekstensi referensi (sama seperti jenis referensi).
LOAS
3
pria ini ada di mana-mana, mencari 8 pertanyaan hari ini, 7 dijawab olehnya.
Kugan Kumar
109

Inilah jawabannya.

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
sumber
12
Dutt, kode Anda seharusnya berfungsi tanpa .ToList () di akhir.
DHornpout
3
oke keren, sekarang kita perlu contoh mengganti baris {} baru dengan pernyataan pilih. var list = sourceList.Select (o => new {o.ModelId, o.PartNumber, o.Quantity}). ToList ();
topwik
@towpse ada solusi tentang itu?
Kiquenet
@ Cut, sampel apa pun jika saya menggunakan metode (fungsi) yang mengembalikan Daftar <T>?
Kiquenet
Sekarang ada metode string.Joindan string interpolasi, jadi tidak perlu menggunakan foreachdan Format.
realsonic
61

Ada banyak cara untuk melakukan ini, tetapi beberapa respons di sini adalah membuat daftar yang mengandung elemen sampah, yang mengharuskan Anda untuk menghapus daftar.

Jika Anda mencari daftar kosong dari tipe generik, gunakan Pilih terhadap Daftar Tuples untuk membuat daftar kosong. Tidak ada elemen yang akan dipakai.

Berikut ini satu-liner untuk membuat daftar kosong:

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

Kemudian Anda dapat menambahkannya menggunakan tipe generik Anda:

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

Sebagai alternatif, Anda dapat melakukan sesuatu seperti di bawah ini untuk membuat daftar kosong (Tapi, saya lebih suka contoh pertama karena Anda dapat menggunakannya untuk koleksi Tuples yang terpopulasi juga):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   
Paul Rouleau
sumber
1
Saya sangat suka cara ini. Terima kasih paul! Itu selalu merupakan hari yang baik ketika Anda dapat menggunakan Tuples! xD
Brady Liles
Saya suka ini. Sangat menyenangkan memiliki semacam deklarasi konkret tentang objek yang akan saya sampaikan.
Morvael
Bagus Saya baru saja selesai menulis kode yang termasuk membersihkan daftar waktu saya untuk menulis ulang
JoshBerke
Terima kasih untuk idenya. Saran, Anda dapat menghindari mengalokasikan daftar dummy jika Anda menggunakanEnumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe
45

Tidak persis, tetapi Anda dapat mengatakan List<object>dan semuanya akan berhasil. Namun, list[0].Idtidak akan berhasil.

Ini akan bekerja saat runtime di C # 4.0 dengan memiliki List<dynamic>, yaitu Anda tidak akan mendapatkan IntelliSense.

Jeff Moser
sumber
Ini tidak diketik dengan kuat, dalam arti bahwa Anda tidak akan memiliki dukungan intellisense kompiler untuk item dalam daftar.
Joel Coehoorn
31
Ini adalah hal yang saya khawatir orang akan lakukan dengan dinamis.
erikkallen
2
Saya tidak mengatakan itu ide yang bagus , tetapi itu mungkin :-) Mungkin ada kebutuhan jika menyimpan objek dari Ruby misalnya.
Jeff Moser
2
Tetapi pada kasus-kasus tersebut tipe sumbernya dinamis, tidak masuk akal untuk menggunakan Daftar <dinamik> untuk tipe anonim.
Dykam
1
Sangat membantu. Terutama jika daftar harus didefinisikan sebelum item anonim ditambahkan ke dalamnya.
Karlth
24

saya tebak

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

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

akan bekerja.

Anda mungkin juga mempertimbangkan untuk menulisnya seperti ini:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
erikkallen
sumber
Ya solusi ini akan membantu menyelesaikan inisialisasi array Anonim. Terima kasih.
DHornpout
1
Cukup beri sedikit <T> setelah nama metode.
Martin
21

Saya biasanya menggunakan yang berikut ini; terutama karena Anda kemudian "mulai" dengan daftar yang kosong.

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

Akhir-akhir ini, saya malah menulis seperti ini:

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

Menggunakan metode pengulangan juga akan memungkinkan Anda untuk melakukan:

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

..yang memberi Anda daftar awal dengan item pertama sudah ditambahkan.

Rostov
sumber
2
Anda tidak perlu memulai dengan daftar kosong - Anda bisa melakukan Range (0,1) dan membuat objek pertama Anda dalam pernyataan pilih menjadi .. objek pertama.
Matius M.
1
Anda tidak perlu memulai dengan daftar kosong - jika Anda tahu item pertama itu (seperti pada contoh) maka Anda benar dalam komentar Anda. Banyak kali saya menggunakan ini untuk mem-parsing file perantara / sumber data dan tidak mengakses item benar pertama sampai menggunakannya dalam skenario proyeksi LINQ (dan dengan demikian tidak perlu memperhitungkan untuk melewatkan catatan pertama).
Rostov
19

Anda dapat melakukan ini dalam kode Anda.

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

Di versi terbaru 4.0, bisa menggunakan dinamis seperti di bawah ini

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

Saya memeriksa IL pada beberapa jawaban. Kode ini secara efisien menyediakan Daftar kosong:

    using System.Linq;
    
    var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
MEC
sumber
1
Ada alasan untuk menolak hasil edit saya? Mengikuti jawaban kembali IEnumerable, sedangkan versi saya kembali List, persis apa yang diminta OP.
Necronomicron
Saya lebih suka pendekatan ini, atau bahkan yang lebih dekat dengan jawaban ini :new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim
8

Ini usahaku.

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

Saya datang dengan ini ketika saya menulis sesuatu yang mirip untuk membuat Daftar Anonim untuk jenis kustom.

user_v
sumber
8

Anda dapat membuat daftar dinamis.

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

"dynamic" diinisialisasi oleh nilai tambah pertama.

Nama Kode Jack
sumber
7

Alih-alih ini:

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

Anda bisa melakukan ini:

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

Namun, Anda akan mendapatkan kesalahan compiletime jika Anda mencoba melakukan sesuatu seperti ini di lingkup lain, meskipun itu berfungsi saat 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); 
    }
}

Masalahnya adalah bahwa hanya anggota Object yang tersedia saat runtime, meskipun intellisense akan menampilkan properti id dan nama .

Dalam .net 4.0 solusinya adalah dengan menggunakan istead dinamis objek dinamis dalam kode di atas.

Solusi lain adalah menggunakan refleksi untuk mendapatkan properti

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

Berikut adalah metode lain untuk membuat Daftar jenis anonim yang memungkinkan Anda untuk memulai dengan daftar kosong, tetapi masih memiliki akses ke IntelliSense.

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

Jika Anda ingin menyimpan item pertama, cukup masukkan satu huruf dalam string.

var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Brackus
sumber
Menggunakan string sebagai inisialisasi array dapat bekerja tetapi merupakan praktik yang sangat buruk
MikeT
Saya akan mengatakan bahwa sebagian besar jawaban di atas bukanlah praktik yang "baik", tapi itu semacam pemberian karena sifat pertanyaan itu. Jenis anonim tidak benar-benar dirancang untuk bekerja dengan cara ini. Saya ingin tahu mengapa metode saya "lebih buruk" daripada yang lain? Sesuatu yang saya lewatkan?
Brackus
Anda benar karena pertanyaannya adalah menanyakan sesuatu yang dengan sendirinya praktik buruk tidak ada jawaban praktik yang baik, tetapi karena Anda menggunakan string untuk menghasilkan array karakter lalu mengubahnya menjadi array apa yang pernah diinginkan pengguna , yang merupakan jenis yang tidak terkait belum lagi menghasilkan tinju berlebihan dan unboxing yang tidak efisien
MikeT
5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
morlock
sumber
5

Ini adalah pertanyaan lama, tapi saya pikir saya akan memasukkan jawaban C # 6 saya. Saya sering harus mengatur data uji yang mudah dimasukkan dalam kode sebagai daftar tupel. Dengan beberapa fungsi ekstensi, dimungkinkan untuk memiliki format yang bagus dan ringkas ini, tanpa mengulangi nama pada setiap entri.

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

Ini memberikan IEnumerable - jika Anda ingin daftar yang dapat Anda tambahkan kemudian tambahkan ToList ().

Keajaiban ini berasal dari ekstensi kustom Tambahkan metode untuk tuple, seperti yang dijelaskan di 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...

}

Satu-satunya hal yang saya tidak suka adalah bahwa jenis dipisahkan dari nama-nama, tetapi jika Anda benar-benar tidak ingin membuat kelas baru maka pendekatan ini masih akan membiarkan Anda memiliki data yang dapat dibaca.

Peter Davidson
sumber
4

Saya sangat terkejut tidak ada yang menyarankan inisialisasi koleksi. Cara ini hanya dapat menambahkan objek ketika daftar dibuat maka namanya namun sepertinya cara terbaik untuk melakukannya. Tidak perlu membuat array lalu mengonversinya menjadi daftar.

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

Anda selalu dapat menggunakan objectalih-alih dynamictetapi mencoba menjaganya dalam cara generik yang benar kemudian dynamiclebih masuk akal.

Tom Dee
sumber
3

Anda dapat melakukannya dengan cara ini:

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

Sepertinya sedikit "hacky" bagi saya, tetapi berhasil - jika Anda benar-benar perlu memiliki daftar dan tidak bisa hanya menggunakan array anonim.

Jermismo
sumber
3

Untuk contoh kedua, di mana Anda harus menginisialisasi yang baru List<T>, satu ide adalah membuat daftar anonim, dan kemudian hapus.

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

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

Atau sebagai metode ekstensi, seharusnya lebih mudah:

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

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

Atau mungkin lebih pendek,

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

Jika Anda menggunakan C # 7 atau lebih tinggi, Anda bisa menggunakan tipe tuple dan bukan tipe anonim.

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

Berasal dari jawaban ini , saya menemukan dua metode yang dapat melakukan tugas:

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

Anda dapat menggunakan metode seperti

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

Jawaban ini memiliki ide yang sama, tetapi saya tidak melihatnya sampai setelah saya membuat metode itu.

BrainStorm.exe
sumber
0

Coba dengan ini:

var result = new List<object>();

foreach (var test in model.ToList()) {
   result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}
Matteo Gariglio
sumber
Di mana daftar jenis anonim?
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
sumber
5
Saya tidak melihat kelas anonim di sini.
Andrew Barber