Dapatkan nilai properti dinamis c # melalui string

182

Saya ingin mengakses nilai dynamicproperti c # dengan string:

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

Bagaimana saya bisa mendapatkan nilai d.value2 ("acak") jika saya hanya memiliki "value2" sebagai string? Dalam javascript, saya bisa melakukan d ["value2"] untuk mengakses nilai ("acak"), tapi saya tidak yakin bagaimana melakukan ini dengan c # dan refleksi. Yang paling dekat saya datang adalah ini:

d.GetType().GetProperty("value2") ... tapi saya tidak tahu bagaimana mendapatkan nilai aktual dari itu.

Seperti biasa, terima kasih atas bantuan Anda!

TimDog
sumber
26
Perhatikan bahwa ini bukan tujuan dari "dinamis" dan bahwa skenario ini tidak berfungsi lebih baik dengan "dinamis" daripada dengan "objek". "dynamic" memungkinkan untuk mengakses properti ketika nama properti diketahui pada waktu kompilasi tetapi tipenya tidak. Karena Anda tidak tahu nama atau jenisnya pada waktu kompilasi, dinamis tidak akan membantu Anda.
Eric Lippert
Mungkin terkait: stackoverflow.com/questions/5877251/… .
DuckMaestro
3
@ EricLippert Saya tahu pertanyaan ini sudah lama tetapi hanya untuk membuat komentar jika seseorang melihatnya di masa depan. Dalam beberapa kasus Anda tidak dapat memilih apakah akan menggunakan dinamis atau objek (misalnya saat menggunakan parser JSON) dan Anda mungkin masih ingin mendapatkan properti dari string (dari file konfigurasi misalnya) jadi penggunaan ini tidak biasa sebagai orang mungkin awalnya berpikir.
Pedrom

Jawaban:

217

Setelah Anda memiliki PropertyInfo(dari GetProperty), Anda perlu menelepon GetValuedan mengirimkan contoh yang Anda ingin dapatkan nilainya. Dalam kasus Anda:

d.GetType().GetProperty("value2").GetValue(d, null);
Adam Robinson
sumber
4
Saya mendapatkan 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}di jendela arloji dengan itu ..?
TimDog
6
Pikirkan GetValue membutuhkan parameter tambahan - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer
3
Apakah ini akan berfungsi pada ExpandoObject dinamis yang sebenarnya daripada jenis anonim? Karena new {}membuat tipe anonim nyata dengan properti yang ditentukan, memanggil GetType / GetProperty masuk akal, tetapi bagaimana dengan ExpandoObject, yang jika Anda panggil GetType, Anda akan mendapatkan tipe yang memiliki properti ExpandoObject, tetapi belum tentu sifat dinamisnya.
Triynko
16
-1. Ini hanya bekerja dengan objek .NET sederhana yang dilemparkan ke dinamis. Ini tidak akan bekerja dengan objek dinamis khusus seperti Expando atau ViewBag yang digunakan ASP.NET MVC
Philipp Munin
8
inilah yang bekerja dengan Expando Object: (((IDictionary <string, object>) x)) ["value1"]
Michael Bahig
39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Tambahkan referensi ke Microsoft.CSharp. Bekerja juga untuk tipe dinamis dan properti dan bidang pribadi.

Sunting : Sementara pendekatan ini berhasil, ada hampir 20 × metode lebih cepat dari perakitan Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
IllidanS4 ingin Monica kembali
sumber
2
Hanya ingin menyebutkan bahwa versi VisualBasic tidak setara dengan versi 'GetProperty' asli Anda (GetProperty benar-benar memanggil GetMember dinamis, yang bekerja bahkan pada objek Python di IronPython).
Trevor Sundberg
apa yang akan menjadi sasaran objek?
Demodave
@Demodave Objek yang ingin Anda panggil properti ( ddalam pertanyaan).
IllidanS4 ingin Monica kembali
➕1 ini berfungsi untuk properti pribadi ketika FastMember dan HyperDescriptor tidak mau
Chris Marisic
@ IllidanS4 ketika Anda membandingkan CallSitekode vs CallByNamekode apakah Anda membandingkan keduanya saat melakukan caching CallSiteinstance? Saya akan curiga biaya metode pertama Anda hampir murni aktivasi Binderdan CallSite, bukan doaTarget()
Chris Marisic
24

Dynamitey adalah .net stdpustaka sumber terbuka , yang memungkinkan Anda menyebutnya seperti dynamickata kunci, tetapi menggunakan string untuk nama properti daripada kompiler yang melakukannya untuk Anda, dan akhirnya sama dengan refleksi dengan cepat (yang hampir tidak secepat cepat) seperti menggunakan kata kunci dinamis, tetapi ini disebabkan oleh overhead tambahan caching secara dinamis, di mana kompiler melakukan cache secara statis).

Dynamic.InvokeGet(d,"value2");
jbtule
sumber
11

Metode termudah untuk mendapatkan a setterdan a getteruntuk properti yang bekerja untuk semua jenis termasuk dynamicdan ExpandoObjectmenggunakan FastMemberyang juga merupakan metode tercepat di sekitar (menggunakan Emit).

Anda bisa mendapatkan TypeAccessorberdasarkan pada jenis tertentu atau ObjectAccessorberdasarkan contoh dari jenis tertentu.

Contoh:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
MaYaN
sumber
8

Sebagian besar waktu ketika Anda meminta objek dinamis, Anda mendapatkan ExpandoObject (tidak dalam contoh yang diketik secara anonim-tapi-statis di atas, tetapi Anda menyebutkan JavaScript dan parser JSON pilihan saya JsonFx, misalnya, menghasilkan ExpandoObjects).

Jika dinamika Anda sebenarnya adalah ExpandoObject, Anda dapat menghindari refleksi dengan mengirimkannya ke IDictionary, seperti yang dijelaskan di http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Setelah Anda menggunakan IDictionary, Anda memiliki akses ke metode yang berguna seperti .Item dan .ContainsKey

Francis Norton
sumber
Sayangnya, harus dilemparkan ke IDictionary dan menggunakan TryGetValue misalnya, menghasilkan objek lama yang dikembalikan. Anda tidak dapat memanfaatkan operator implisit pada saat itu, karena mereka hanya dianggap pada waktu kompilasi. Misalnya, jika saya memiliki kelas Int64Proxy dengan konversi implisit ke Int64 ?, maka Int64? i = data.value; //data is ExpandoObjectsecara otomatis akan mencari dan memanggil operator implisit. Di sisi lain, jika saya harus menggunakan IDictionary untuk menguji apakah bidang "nilai" ada, saya akan mendapatkan objek kembali yang tidak akan dilemparkan tanpa kesalahan ke Int64 ?.
Triynko
5

GetProperty / GetValue tidak berfungsi untuk data Json, itu selalu menghasilkan pengecualian nol, namun, Anda dapat mencoba pendekatan ini:

Serialize objek Anda menggunakan JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Kemudian mengaksesnya langsung melemparkannya kembali ke string:

var pn = (string)z["DynamicFieldName"];

Mungkin bekerja langsung menerapkan Convert.ToString (permintaan) ["DynamicFieldName"], namun saya belum menguji.

Anderson
sumber
2
Metode ini menghasilkan kesalahan: kesalahan CS0021: Tidak dapat menerapkan pengindeksan dengan [] ke ekspresi tipe 'objek'. Gunakan new JavaScriptSerializer().Deserialize<object>(json);untuk sampai ke "properti" dengan cara yang Anda sarankan
Kris Kilton
4

d.GetType (). GetProperty ("value2")

mengembalikan objek PropertyInfo.

Jadi lakukan

propertyInfo.GetValue(d)
James Gaunt
sumber
2
terima kasih, ini adalah jawaban yang benar, tetapi seperti yang disebutkan di atas, GetValue(d)perluGetValue(d,null)
TimDog
4

Ini adalah cara saya mendapatkan nilai dari nilai properti dinamis:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }
Marcelo Lima Braga
sumber
1

Untuk mendapatkan properti dari dokumen dinamis saat .GetType()kembali null, coba ini:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
yzhai bruin
sumber
0

Di .Net inti 3.1 Anda dapat mencoba seperti ini

d?.value2 , d?.value3
vyeluri5
sumber
0

Mirip dengan jawaban yang diterima, Anda juga dapat mencoba GetFieldbukan GetProperty.

d.GetType().GetField("value2").GetValue(d);

Bergantung pada bagaimana sebenarnya Typediterapkan, ini dapat bekerja ketika GetProperty () tidak dan bahkan bisa lebih cepat.

Efreeto
sumber
FYI Perbedaan antara Properti dan Lapangan di C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto