Bagaimana cara mengakses properti tipe anonim di C #?
125
Aku punya ini:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_"+ d.Id});
... dan saya ingin tahu apakah saya dapat mengambil properti "Dicentang" dari objek anonim. Saya tidak yakin apakah ini mungkin. Mencoba melakukan ini:
if (nodes.Any(n => n["Checked"] == false)) ... tapi tidak berhasil.
Jika Anda menginginkan daftar jenis anonim yang diketik dengan kuat, Anda juga perlu membuat daftar jenis anonim. Cara termudah untuk melakukannya adalah dengan memproyeksikan urutan seperti array ke dalam daftar, mis
var nodes =(new[]{new{Checked=false,/* etc */}}).ToList();
Kemudian Anda akan dapat mengaksesnya seperti:
nodes.Any(n => n.Checked);
Karena cara kerja kompilator, berikut ini seharusnya juga berfungsi setelah Anda membuat daftar, karena tipe anonim memiliki struktur yang sama sehingga mereka juga memiliki tipe yang sama. Saya tidak memiliki kompiler untuk memverifikasi ini.
Jika Anda menyimpan objek sebagai tipe object, Anda perlu menggunakan refleksi. Ini berlaku untuk semua jenis objek, anonim atau lainnya. Pada objek o, Anda bisa mendapatkan tipenya:
Type t = o.GetType();
Kemudian dari situ Anda mencari properti:
PropertyInfo p = t.GetProperty("Foo");
Kemudian dari situ Anda bisa mendapatkan nilai:
object v = p.GetValue(o,null);
Jawaban ini sudah lama menunggu pembaruan untuk C # 4:
dynamic d = o;object v = d.Foo;
Dan sekarang alternatif lain di C # 6:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o,null);
Perhatikan bahwa dengan menggunakan ?.kita menyebabkan yang dihasilkan vmenjadi nulltiga situasi yang berbeda!
oadalah null, jadi tidak ada objek sama sekali
oadalah non- nulltetapi tidak memiliki propertiFoo
omemiliki properti Footetapi nilai riilnya kebetulan null.
Jadi ini tidak sama dengan contoh sebelumnya, tetapi mungkin masuk akal jika Anda ingin memperlakukan ketiga kasus dengan sama.
Belum pernah menggunakan dinamika sebelumnya sampai sekarang, pembaruan yang bagus untuk .NET 4.0
Alan
dalam solusi c # 4 Anda akan mendapatkan pengecualian runtime jika properti tidak ada ( object v = d.Foo), sedangkan GetValue(o, null)akan menjadi null jika tidak ada.
YaakovHatam
1
Tidak, GetPropertyakan kembali null, dan GetValueakan melempar jika diteruskan null, jadi efek keseluruhan adalah pengecualian. Versi C # 4.0 memberikan pengecualian yang lebih deskriptif.
Daniel Earwicker
4
Jika Anda menggunakan dinamika dalam perakitan yang berbeda dari sumbernya maka Anda perlu menggunakan [InternalsVisibleTo]
Sarath
2
@DanielEarwicker terima kasih sudah selesai. Ini juga berlaku untuk tipe anonim. Karena semua properti yang dihasilkan untuk tipe anonim bersifat internal.
Sarath
13
Anda bisa mengulangi properti tipe anonim menggunakan Refleksi; lihat apakah ada properti "Dicentang" dan jika ada, dapatkan nilainya.
foreach(object o in nodes){Type t = o.GetType();PropertyInfo[] pi = t.GetProperties();foreach(PropertyInfo p in pi){if(p.Name=="Checked"&&!(bool)p.GetValue(o))Console.WriteLine("awesome!");}}
Jika Anda hanya membutuhkan satu properti dan Anda sudah tahu namanya, tidak ada gunanya memeriksa semuanya; cukup gunakan GetProperty dan GetValue. Juga, System.out.println adalah Java, bukan C # ...
Chris Charabaruk
Ups, ini benar, Chris! Agak memalukan ... diperbaiki sekarang.
glennkentwell
6
Jawaban yang diterima dengan benar menjelaskan bagaimana daftar harus dideklarasikan dan sangat direkomendasikan untuk sebagian besar skenario.
Tetapi saya menemukan skenario yang berbeda, yang juga mencakup pertanyaan yang diajukan. Bagaimana jika Anda harus menggunakan daftar objek yang ada, seperti ViewData["htmlAttributes"]di MVC ? Bagaimana Anda dapat mengakses propertinya (biasanya dibuat melalui new { @style="width: 100px", ... })?
Untuk skenario yang sedikit berbeda ini, saya ingin berbagi dengan Anda apa yang saya temukan. Dalam solusi di bawah ini, saya mengasumsikan deklarasi berikut untuk nodes:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_1"});
1. Solusi dengan dinamis
Di C # 4.0 dan versi yang lebih tinggi , Anda cukup mentransmisikan ke dinamis dan menulis:
if(nodes.Any(n =>((dynamic)n).Checked==false))Console.WriteLine("found not checked element!");
Catatan: Ini menggunakan pengikatan akhir, yang berarti ia akan mengenali hanya pada waktu proses jika objek tidak memiliki Checkedproperti dan RuntimeBinderExceptiondalam kasus ini melontarkan a - jadi jika Anda mencoba menggunakan properti yang tidak ada Checked2, Anda akan mendapatkan pesan berikut di runtime:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'" .
2. Solusi dengan refleksi
Solusi dengan refleksi bekerja baik dengan kompiler C # lama dan baru versi . Untuk versi C # lama harap perhatikan petunjuk di akhir jawaban ini.
Latar Belakang
Sebagai titik awal, saya menemukan jawaban yang bagus di sini . Idenya adalah untuk mengubah tipe data anonim menjadi kamus dengan menggunakan refleksi. Kamus memudahkan untuk mengakses properti, karena namanya disimpan sebagai kunci (Anda dapat mengaksesnya sepertimyDict["myProperty"] ).
Terinspirasi oleh kode pada tautan di atas, saya membuat kelas ekstensi yang disediakan GetProp, UnanonymizePropertiesdan UnanonymizeListItemssebagai metode ekstensi, yang menyederhanakan akses ke properti anonim. Dengan kelas ini Anda cukup melakukan kueri sebagai berikut:
if(nodes.UnanonymizeListItems().Any(n =>(bool)n["Checked"]==false)){Console.WriteLine("found not checked element!");}
atau Anda dapat menggunakan ekspresi nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()sebagaiif kondisi, yang memfilter secara implisit lalu memeriksa apakah ada elemen yang dikembalikan.
Untuk mendapatkan objek pertama yang berisi properti "Dicentang" dan mengembalikan properti "kedalaman", Anda dapat menggunakan:
var depth = nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
atau lebih pendek: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Catatan: Jika Anda memiliki daftar objek yang tidak selalu berisi semua properti (misalnya, beberapa tidak berisi properti "Dicentang"), dan Anda masih ingin membuat kueri berdasarkan nilai "Dicentang", Anda dapat melakukan hal ini:
if(nodes.UnanonymizeListItems(x =>{var y =((bool?)x.GetProp("Checked",true));return y.HasValue&& y.Value==false;}).Any()){Console.WriteLine("found not checked element!");}
Ini mencegah, yang KeyNotFoundExceptionterjadi jika properti "Dicentang" tidak ada.
Kelas di bawah ini berisi metode ekstensi berikut:
UnanonymizeProperties: Digunakan untuk membatalkan anonimitas properti yang terdapat dalam suatu objek. Metode ini menggunakan refleksi. Ini mengubah objek menjadi kamus yang berisi properti dan nilainya.
UnanonymizeListItems: Digunakan untuk mengubah daftar objek menjadi daftar kamus yang berisi properti. Ini mungkin secara opsional berisi ekspresi lambda untuk difilter sebelumnya.
GetProp: Digunakan untuk mengembalikan satu nilai yang cocok dengan nama properti yang diberikan. Mengizinkan untuk memperlakukan properti yang tidak ada sebagai nilai null (benar) daripada sebagai KeyNotFoundException (salah)
Untuk contoh di atas, yang diperlukan hanyalah Anda menambahkan kelas ekstensi di bawah ini:
publicstaticclassAnonymousTypeExtensions{// makes properties of object accessible publicstaticIDictionaryUnanonymizeProperties(thisobject obj){Type type = obj?.GetType();var properties = type?.GetProperties()?.Select(n => n.Name)?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj,null));return properties;}// converts object list into list of properties that meet the filterCriteriapublicstaticList<IDictionary>UnanonymizeListItems(thisList<object> objectList,Func<IDictionary<string,object>,bool> filterCriteria=default){var accessibleList =newList<IDictionary>();foreach(object obj in objectList){var props = obj.UnanonymizeProperties();if(filterCriteria ==default|| filterCriteria((IDictionary<string,object>)props)==true){ accessibleList.Add(props);}}return accessibleList;}// returns specific property, i.e. obj.GetProp(propertyName)// requires prior usage of AccessListItems and selection of one element, because// object needs to be a IDictionary<string, object>publicstaticobjectGetProp(thisobject obj,string propertyName,bool treatNotFoundAsNull =false){try{return((System.Collections.Generic.IDictionary<string,object>)obj)?[propertyName];}catch(KeyNotFoundException){if(treatNotFoundAsNull)returndefault(object);elsethrow;}}}
Petunjuk: Kode di atas adalah dengan menggunakan null-bersyarat operator, tersedia sejak C # versi 6.0 - Jika Anda bekerja dengan lebih tua C # compiler (misalnya C # 3.0), cukup mengganti ?.dengan .dan ?[oleh [mana-mana, misalnya
var depth = nodes.UnanonymizeListItems().FirstOrDefault(n => n.Contains("Checked"))["depth"];
Jika Anda tidak dipaksa untuk menggunakan kompiler C # yang lebih lama, pertahankan apa adanya, karena menggunakan null-conditionals membuat penanganan null menjadi lebih mudah.
Catatan: Seperti solusi lain dengan dinamis, solusi ini juga menggunakan pengikatan akhir, tetapi dalam kasus ini Anda tidak mendapatkan pengecualian - solusi ini tidak akan menemukan elemen jika Anda merujuk ke properti yang tidak ada, selama saat Anda mempertahankan operator bersyarat null .
Apa yang mungkin berguna untuk beberapa aplikasi adalah bahwa properti tersebut dirujuk melalui string dalam solusi 2, sehingga dapat dijadikan parameter.
object v = d.Foo
), sedangkanGetValue(o, null)
akan menjadi null jika tidak ada.GetProperty
akan kembalinull
, danGetValue
akan melempar jika diteruskannull
, jadi efek keseluruhan adalah pengecualian. Versi C # 4.0 memberikan pengecualian yang lebih deskriptif.Anda bisa mengulangi properti tipe anonim menggunakan Refleksi; lihat apakah ada properti "Dicentang" dan jika ada, dapatkan nilainya.
Lihat entri blog ini: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
Jadi sesuatu seperti:
sumber
Jawaban yang diterima dengan benar menjelaskan bagaimana daftar harus dideklarasikan dan sangat direkomendasikan untuk sebagian besar skenario.
Tetapi saya menemukan skenario yang berbeda, yang juga mencakup pertanyaan yang diajukan. Bagaimana jika Anda harus menggunakan daftar objek yang ada, seperti
ViewData["htmlAttributes"]
di MVC ? Bagaimana Anda dapat mengakses propertinya (biasanya dibuat melaluinew { @style="width: 100px", ... }
)?Untuk skenario yang sedikit berbeda ini, saya ingin berbagi dengan Anda apa yang saya temukan. Dalam solusi di bawah ini, saya mengasumsikan deklarasi berikut untuk
nodes
:1. Solusi dengan dinamis
Di C # 4.0 dan versi yang lebih tinggi , Anda cukup mentransmisikan ke dinamis dan menulis:
Catatan: Ini menggunakan pengikatan akhir, yang berarti ia akan mengenali hanya pada waktu proses jika objek tidak memiliki
Checked
properti danRuntimeBinderException
dalam kasus ini melontarkan a - jadi jika Anda mencoba menggunakan properti yang tidak adaChecked2
, Anda akan mendapatkan pesan berikut di runtime:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.2. Solusi dengan refleksi
Solusi dengan refleksi bekerja baik dengan kompiler C # lama dan baru versi . Untuk versi C # lama harap perhatikan petunjuk di akhir jawaban ini.
Latar Belakang
Sebagai titik awal, saya menemukan jawaban yang bagus di sini . Idenya adalah untuk mengubah tipe data anonim menjadi kamus dengan menggunakan refleksi. Kamus memudahkan untuk mengakses properti, karena namanya disimpan sebagai kunci (Anda dapat mengaksesnya seperti
myDict["myProperty"]
).Terinspirasi oleh kode pada tautan di atas, saya membuat kelas ekstensi yang disediakan
GetProp
,UnanonymizeProperties
danUnanonymizeListItems
sebagai metode ekstensi, yang menyederhanakan akses ke properti anonim. Dengan kelas ini Anda cukup melakukan kueri sebagai berikut:atau Anda dapat menggunakan ekspresi
nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
sebagaiif
kondisi, yang memfilter secara implisit lalu memeriksa apakah ada elemen yang dikembalikan.Untuk mendapatkan objek pertama yang berisi properti "Dicentang" dan mengembalikan properti "kedalaman", Anda dapat menggunakan:
atau lebih pendek:
nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Catatan: Jika Anda memiliki daftar objek yang tidak selalu berisi semua properti (misalnya, beberapa tidak berisi properti "Dicentang"), dan Anda masih ingin membuat kueri berdasarkan nilai "Dicentang", Anda dapat melakukan hal ini:
Ini mencegah, yang
KeyNotFoundException
terjadi jika properti "Dicentang" tidak ada.Kelas di bawah ini berisi metode ekstensi berikut:
UnanonymizeProperties
: Digunakan untuk membatalkan anonimitas properti yang terdapat dalam suatu objek. Metode ini menggunakan refleksi. Ini mengubah objek menjadi kamus yang berisi properti dan nilainya.UnanonymizeListItems
: Digunakan untuk mengubah daftar objek menjadi daftar kamus yang berisi properti. Ini mungkin secara opsional berisi ekspresi lambda untuk difilter sebelumnya.GetProp
: Digunakan untuk mengembalikan satu nilai yang cocok dengan nama properti yang diberikan. Mengizinkan untuk memperlakukan properti yang tidak ada sebagai nilai null (benar) daripada sebagai KeyNotFoundException (salah)Untuk contoh di atas, yang diperlukan hanyalah Anda menambahkan kelas ekstensi di bawah ini:
Petunjuk: Kode di atas adalah dengan menggunakan null-bersyarat operator, tersedia sejak C # versi 6.0 - Jika Anda bekerja dengan lebih tua C # compiler (misalnya C # 3.0), cukup mengganti
?.
dengan.
dan?[
oleh[
mana-mana, misalnyaJika Anda tidak dipaksa untuk menggunakan kompiler C # yang lebih lama, pertahankan apa adanya, karena menggunakan null-conditionals membuat penanganan null menjadi lebih mudah.
Catatan: Seperti solusi lain dengan dinamis, solusi ini juga menggunakan pengikatan akhir, tetapi dalam kasus ini Anda tidak mendapatkan pengecualian - solusi ini tidak akan menemukan elemen jika Anda merujuk ke properti yang tidak ada, selama saat Anda mempertahankan operator bersyarat null .
Apa yang mungkin berguna untuk beberapa aplikasi adalah bahwa properti tersebut dirujuk melalui string dalam solusi 2, sehingga dapat dijadikan parameter.
sumber
Baru-baru ini, saya mengalami masalah yang sama dalam .NET 3.5 (tidak ada dinamika yang tersedia). Inilah cara saya memecahkan:
Diadaptasi dari suatu tempat di stackoverflow:
Sekarang kembalikan objek melalui cast:
sumber