Bagaimana cara mendeteksi jika ada properti pada ExpandoObject?

188

Dalam javascript Anda dapat mendeteksi apakah suatu properti didefinisikan dengan menggunakan kata kunci yang tidak ditentukan:

if( typeof data.myProperty == "undefined" ) ...

Bagaimana Anda melakukan ini dalam C # menggunakan kata kunci dinamis dengan ExpandoObjectdan tanpa membuang pengecualian?

Softlion
sumber
5
@CodeInChaos: Perhatikan bahwa kode yang ditampilkan tidak memeriksa nilai data.myProperty; memeriksa apa yang typeof data.myPropertykembali. Benar bahwa data.myPropertymungkin ada dan diatur undefined, tetapi dalam kasus itu, typeofakan mengembalikan sesuatu selain "undefined". Jadi kode ini berfungsi.
Aasmund Eldhuset

Jawaban:

181

Menurut MSDN deklarasi menunjukkan sedang mengimplementasikan IDictionary:

public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
    IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
    IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged

Anda dapat menggunakan ini untuk melihat apakah seorang anggota didefinisikan:

var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
    // expandoObject.SomeMember exists.
}
Dykam
sumber
3
Untuk membuat pemeriksaan ini lebih sederhana, saya telah membebani TryGetValue dan membuatnya selalu kembali benar, menetapkan nilai kembali ke "tidak terdefinisi" jika properti tidak ditentukan. if (someObject.someParam! = "undefined") ... Dan itu bekerja :)
Softlion
Juga mungkin :), tapi saya pikir Anda bermaksud "ditimpa" alih-alih kelebihan beban.
Dykam
ya. Saya telah mengubah "undefined" menjadi nilai const objek khusus yang saya buat di tempat lain. Ini mencegah masalah casting: p
Softlion
1
Saya percaya solusi ini masih terkini; jangan mengambil kata siapa pun untuk harga refleksi - uji sendiri dan lihat apakah Anda mampu membelinya
nik.shornikov
1
@ BlueRaja-DannyPflughoeft Ya, benar. Tanpa para pemain itu hanya akan menjadi doa yang dinamis, dengan kasus yang Anda dapatkan di internal. Lebih khusus lagi, ini diterapkan secara eksplisit: github.com/mono/mono/blob/master/mcs/class/dlr/Runtime/…
Dykam
28

Perbedaan penting perlu dibuat di sini.

Sebagian besar jawaban di sini khusus untuk ExpandoObject yang disebutkan dalam pertanyaan. Tetapi penggunaan umum (dan alasan untuk mendarat pada pertanyaan ini saat mencari) adalah ketika menggunakan ASP.Net MVC ViewBag. Itu adalah implementasi kustom / subkelas DynamicObject, yang tidak akan membuang Pengecualian ketika Anda memeriksa sembarang nama properti arbitrer untuk null. Misalkan Anda dapat mendeklarasikan properti seperti:

@{
    ViewBag.EnableThinger = true;
}

Lalu anggaplah Anda ingin memeriksa nilainya, dan apakah itu disetel - apakah ada. Berikut ini adalah valid, akan dikompilasi, tidak akan memberikan pengecualian, dan memberi Anda jawaban yang benar:

if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
    // Do some stuff when EnableThinger is true
}

Sekarang singkirkan deklarasi EnableThinger. Kode yang sama mengkompilasi dan berjalan dengan benar. Tidak perlu refleksi.

Tidak seperti ViewBag, ExpandoObject akan melempar jika Anda memeriksa nol pada properti yang tidak ada. Untuk mendapatkan fungsionalitas MVC ViewBag yang lebih lembut dari dynamicobjek Anda, Anda harus menggunakan implementasi dinamis yang tidak melempar.

Anda bisa menggunakan implementasi yang tepat di MVC ViewBag:

. . .
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    result = ViewData[binder.Name];
    // since ViewDataDictionary always returns a result even if the key does not exist, always return true
    return true;
}
. . .

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/DynamicViewDataDictionary.cs

Anda dapat melihatnya diikat ke Tampilan MVC di sini, di MVC ViewPage:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs

Kunci perilaku anggun DynamicViewDatationary adalah implementasi Kamus di ViewDataDictionary, di sini:

public object this[string key]
{
    get
    {
        object value;
        _innerDictionary.TryGetValue(key, out value);
        return value;
    }
    set { _innerDictionary[key] = value; }
}

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/ViewDataDictionary.cs

Dengan kata lain, itu selalu mengembalikan nilai untuk semua kunci, terlepas dari apa yang ada di dalamnya - itu hanya mengembalikan nol ketika tidak ada di sana. Tapi, ViewDataDictionary memiliki beban terikat dengan Model MVC, jadi lebih baik untuk menghapus hanya bagian kamus yang anggun untuk digunakan di luar Tampilan MVC.

Terlalu lama untuk benar-benar memposting semua nyali di sini - sebagian besar hanya menerapkan IDictionary - tapi di sini ada objek dinamis (kelas DDict) yang tidak membuang pemeriksaan nol pada properti yang belum dideklarasikan, di Github:

https://github.com/b9chris/GracefulDynamicDictionary

Jika Anda hanya ingin menambahkannya ke proyek Anda melalui NuGet, namanya GracefulDynamicDictionary .

Chris Moschini
sumber
Mengapa Anda memilih DynamicDictionary karena tidak menggunakan refleksi?
Softlion
maka Anda dapat memilihnya karena ini adalah solusi yang sama :)
Softlion
3
Ini pastinya bukan solusi yang sama.
Chris Moschini
"Anda harus membuat subclass serupa yang tidak dibuang ketika properti tidak ditemukan." => itu! Oh tidak. Solusi saya lebih baik. Itu melempar - karena kita menginginkannya, DAN itu juga tidak bisa melempar jika TryXX digunakan;
Softlion
1
INI, INI persis mengapa saya di sini, saya tidak tahu mengapa beberapa kode (viewbag) TIDAK rusak. Terima kasih.
Adam Tolley
11

Saya menjawab pertanyaan yang sangat mirip baru-baru ini: Bagaimana cara saya merefleksikan anggota objek dinamis?

Singkatnya, ExpandoObject bukan satu-satunya objek dinamis yang mungkin Anda dapatkan. Refleksi akan berfungsi untuk tipe statis (tipe yang tidak mengimplementasikan IDynamicMetaObjectProvider). Untuk tipe yang mengimplementasikan antarmuka ini, refleksi pada dasarnya tidak berguna. Untuk ExpandoObject, Anda cukup memeriksa apakah properti didefinisikan sebagai kunci dalam kamus yang mendasarinya. Untuk implementasi lain, ini mungkin menantang dan terkadang satu-satunya cara adalah bekerja dengan pengecualian. Untuk detailnya, ikuti tautan di atas.

Alexandra Rusina
sumber
11

DIPERBARUI: Anda dapat menggunakan delegasi dan mencoba untuk mendapatkan nilai dari properti objek dinamis jika ada. Jika tidak ada properti, cukup tangkap pengecualian dan kembalikan false.

Coba lihat, ini berfungsi dengan baik untuk saya:

class Program
{
    static void Main(string[] args)
    {
        dynamic userDynamic = new JsonUser();

        Console.WriteLine(IsPropertyExist(() => userDynamic.first_name));
        Console.WriteLine(IsPropertyExist(() => userDynamic.address));
        Console.WriteLine(IsPropertyExist(() => userDynamic.last_name));
    }

    class JsonUser
    {
        public string first_name { get; set; }
        public string address
        {
            get
            {
                throw new InvalidOperationException("Cannot read property value");
            }
        }
    }

    static bool IsPropertyExist(GetValueDelegate getValueMethod)
    {
        try
        {
            //we're not interesting in the return value. What we need to know is whether an exception occurred or not
            getValueMethod();
            return true;
        }
        catch (RuntimeBinderException)
        {
            // RuntimeBinderException occurred during accessing the property
            // and it means there is no such property         
            return false;
        }
        catch
        {
            //property exists, but an exception occurred during getting of a value
            return true;
        }
    }

    delegate string GetValueDelegate();
}

Output dari kode adalah sebagai berikut:

True
True
False
Alexander G
sumber
2
@marklam menangkap semua pengecualian itu buruk ketika Anda tidak tahu apa yang menyebabkan pengecualian itu. Dalam kasus kami tidak apa-apa karena kami memperkirakan kemungkinan tidak adanya bidang.
Alexander G
3
jika Anda tahu apa yang menyebabkan pengecualian, Anda juga harus tahu jenisnya, jadi tangkap (ApapunException) Kalau tidak, kode Anda akan diam-diam melanjutkan bahkan jika Anda mendapat pengecualian yang tidak terduga - seperti OutOfMemoryException misalnya.
Marklam
4
Anda dapat lulus dalam setiap rajin IsPropertyExist. Dalam contoh ini, Anda tahu seseorang dapat melempar InvalidOperationException. Dalam praktiknya, Anda tidak tahu pengecualian apa yang dapat diberikan. +1 untuk menangkal kultus kargo.
piedar
2
Solusi ini tidak dapat diterima jika kinerja penting, misalnya jika digunakan dalam satu lingkaran dengan 500+ iterasi yang ditambahkan dan dapat menyebabkan penundaan selama beberapa detik. Setiap kali pengecualian tertangkap, tumpukan harus disalin ke objek pengecualian
Jim109
1
Re: Performance: debugger yang dilampirkan dan Console.WriteLine adalah bit yang lambat. 10.000 iterasi di sini membutuhkan waktu kurang dari 200 ms (dengan 2 pengecualian per iterasi). Tes yang sama tanpa pengecualian membutuhkan beberapa milidetik. Itu berarti jika Anda mengharapkan penggunaan kode ini hanya akan jarang kehilangan properti, atau jika Anda menyebutnya beberapa kali, atau dapat men-cache hasil, maka harap menyadari bahwa semuanya memiliki tempatnya dan tidak ada yang terlalu Peringatan yang dimundurkan di sini penting.
Kekacauan
10

Saya ingin membuat metode ekstensi sehingga saya bisa melakukan sesuatu seperti:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}

... tetapi Anda tidak dapat membuat ekstensi ExpandoObjectsesuai dengan dokumentasi C # 5 (info lebih lanjut di sini ).

Jadi saya akhirnya membuat pembantu kelas:

public static class ExpandoObjectHelper
{
    public static bool HasProperty(ExpandoObject obj, string propertyName)
    {
        return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
    }
}

Untuk menggunakannya:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}
Maxime
sumber
4
suara untuk komentar yang berguna dan tautan pada ekstensi untuk ExpandoObject.
Roberto
1

Mengapa Anda tidak ingin menggunakan Reflection untuk mendapatkan set jenis properyes? Seperti ini

 dynamic v = new Foo();
 Type t = v.GetType();
 System.Reflection.PropertyInfo[] pInfo =  t.GetProperties();
 if (Array.Find<System.Reflection.PropertyInfo>(pInfo, p => { return p.Name == "PropName"; }).    GetValue(v,  null) != null))
 {
     //PropName initialized
 } 
Gunung Vokinneberg
sumber
Saya tidak yakin apakah itu akan mengembalikan properti yang ditambahkan secara dinamis, dugaan saya adalah bahwa ia mengembalikan metode objek Dinamis.
Dykam
1

Metode ekstensi ini memeriksa keberadaan properti dan kemudian mengembalikan nilai atau nol. Ini berguna jika Anda tidak ingin aplikasi Anda melemparkan pengecualian yang tidak perlu, setidaknya yang dapat Anda bantu.

    public static object Value(this ExpandoObject expando, string name)
    {
        var expandoDic = (IDictionary<string, object>)expando;
        return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
    }

Jika bisa digunakan seperti itu:

  // lookup is type 'ExpandoObject'
  object value = lookup.Value("MyProperty");

atau jika variabel lokal Anda 'dinamis', Anda harus melemparkannya ke ExpandoObject terlebih dahulu.

  // lookup is type 'dynamic'
  object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
realdanielbyrne
sumber
1

Bergantung pada use case Anda, jika null dapat dianggap sama dengan undefined, Anda dapat mengubah ExpandoObject Anda menjadi DynamicJsonObject.

    dynamic x = new System.Web.Helpers.DynamicJsonObject(new ExpandoObject());
    x.a = 1;
    x.b = 2.50;
    Console.WriteLine("a is " + (x.a ?? "undefined"));
    Console.WriteLine("b is " + (x.b ?? "undefined"));
    Console.WriteLine("c is " + (x.c ?? "undefined"));

Keluaran:

a is 1
b is 2.5
c is undefined
Wolfgang Grinfeld
sumber
-2
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Kasper Roma
sumber
-3

Hai teman-teman berhenti menggunakan Refleksi untuk semua hal yang menghabiskan banyak siklus CPU.

Ini solusinya:

public class DynamicDictionary : DynamicObject
{
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public int Count
    {
        get
        {
            return dictionary.Count;
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;

        if (!dictionary.TryGetValue(binder.Name, out result))
            result = "undefined";

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }
}
Softlion
sumber
4
Ini menunjukkan bagaimana menerapkan objek dinamis, bukan bagaimana melihatnya properti keluar pada objek dinamis.
Matt Warren
Anda dapat memeriksa apakah instance dinamis memiliki properti dengan melakukan pemeriksaan nol terhadap properti yang dimaksud.
ctorx
2
"Ini menunjukkan bagaimana mengimplementasikan objek dinamis": ya sebenarnya itu. Solusi untuk pertanyaan ini adalah: tidak ada solusi umum karena tergantung pada implementasinya.
Softlion
@Softlion Tidak, solusinya adalah bahwa kita harus berhenti menggunakan
nik.shornikov
@Softlion Apa gunanya metode Tryxxx? TryGet tidak akan pernah mengembalikan false ketika tidak menemukan properti, jadi Anda masih harus memeriksa hasilnya. Kembalinya tidak berguna. Di TrySet, jika kunci tidak ada, maka itu akan melemparkan pengecualian alih-alih salah. Saya tidak mengerti mengapa Anda bahkan akan menggunakan ini sebagai jawaban, jika Anda sendiri menulis di sini di komentar "Solusi untuk pertanyaan ini adalah: tidak ada solusi umum karena tergantung pada implementasinya", itu juga tidak benar. Lihatlah jawaban Dykam untuk solusi nyata.
pqsk
-5

Coba yang ini

public bool PropertyExist(object obj, string propertyName)
{
 return obj.GetType().GetProperty(propertyName) != null;
}
Venkat
sumber
3
Ini akan memeriksa keberadaan properti objek yang disembunyikan di bawah nama dinamis, yang merupakan detail implementasi. Sudahkah Anda memverifikasi solusi Anda dalam kode nyata sebelum memposting? Seharusnya tidak bekerja sama sekali.
Softlion
Saya telah menggunakan potongan kode ini dalam skenario waktu nyata. Itu bekerja dengan baik.
Venkat
6
Memberi saya null sepanjang waktu, bahkan jika properti itu ada.
atlantis
Ini akan bekerja dengan objek dasar, tetapi tidak dengan ExpandoObjects. Dinamika, tidak yakin.
Joe
1
Untuk mengonfirmasi, ini juga tidak berfungsi dengan dynamicobjek (selalu kembali null).
Hilang Coding