Perbedaan antara ExpandoObject, DynamicObject dan dynamic

170

Apa perbedaan antara System.Dynamic.ExpandoObject, System.Dynamic.DynamicObjectdan dynamic?

Dalam situasi apa Anda menggunakan tipe ini?

M4N
sumber

Jawaban:

154

Kata dynamickunci tersebut digunakan untuk mendeklarasikan variabel yang harus diikat.
Jika Anda ingin menggunakan pengikatan yang lebih lambat, untuk semua tipe nyata atau yang dibayangkan, Anda menggunakan dynamickata kunci dan sisanya adalah kompiler.

Saat Anda menggunakan dynamickata kunci untuk berinteraksi dengan mesin virtual normal, DLR melakukan panggilan yang terlambat ke metode normal mesin virtual tersebut.

The IDynamicMetaObjectProviderantarmuka memungkinkan kelas untuk mengambil kendali dari perilaku akhir-terikat nya.
Ketika Anda menggunakan dynamickata kunci untuk berinteraksi dengan IDynamicMetaObjectProviderimplementasi, DLR memanggil IDynamicMetaObjectProvidermetode dan objek itu sendiri memutuskan apa yang harus dilakukan.

The ExpandoObjectdan DynamicObjectkelas implementasi dari IDynamicMetaObjectProvider.

ExpandoObjectadalah kelas sederhana yang memungkinkan Anda untuk menambahkan anggota ke instance dan menggunakannya sebagai dynamicsekutu.
DynamicObjectadalah implementasi yang lebih maju yang dapat diwarisi untuk dengan mudah memberikan perilaku yang disesuaikan.

Slaks
sumber
2
Apa yang akan menjadi tempat yang baik untuk belajar lebih banyak tentang ini? Bukan API tetapi mengapa di balik API? mis. Mengapa ExpandoObject tidak berasal dari DynamicObject, yang terlihat tipe dasar defacto untuk pemrograman berbasis 'method_missing' ruby.
Gishu
4
Bisakah Anda menambahkan beberapa contoh penggunaan jika memungkinkan? Misalnya, bagaimana saya menggunakan DynamicObject, dan apa manfaatnya?
oɔɯǝɹ
10
Jawaban yang bagus tanpa contoh seperti ini seperti kue tanpa krim di atasnya.
Teoman shipahi
68

Saya akan mencoba memberikan jawaban yang lebih jelas untuk pertanyaan ini, untuk menjelaskan dengan jelas apa perbedaan antara dinamis, ExpandoObjectdan DynamicObject.

Sangat cepat, dynamicadalah kata kunci. Ini bukan tipe per-se. Ini adalah kata kunci yang memberitahu kompiler untuk mengabaikan pengecekan tipe statis pada waktu desain dan alih-alih menggunakan pengikatan yang terlambat pada waktu berjalan. Jadi kita tidak akan menghabiskan banyak waktu dynamicdi sisa jawaban ini.

ExpandoObjectdan DynamicObjectmemang tipe. Di PERMUKAAN, mereka terlihat sangat mirip satu sama lain. Kedua kelas menerapkan IDynamicMetaObjectProvider. Namun, gali lebih dalam dan Anda akan menemukan bahwa mereka TIDAK serupa.

DynamicObject adalah implementasi parsial yang IDynamicMetaObjectProvidermurni dimaksudkan sebagai titik awal bagi pengembang untuk mengimplementasikan tipe kustom mereka sendiri yang mendukung pengiriman dinamis dengan penyimpanan yang mendasari kustom dan perilaku pengambilan untuk membuat pengiriman dinamis berfungsi.

  1. DynamicObject tidak dapat dibangun secara langsung.
  2. Anda HARUS memperluas DynamicObject agar dapat digunakan untuk Anda sebagai pengembang.
  3. Ketika Anda memperluas DynamicObject Anda sekarang dapat memberikan perilaku CUSTOM mengenai bagaimana Anda ingin pengiriman dinamis untuk menyelesaikan data yang disimpan secara internal dalam representasi data yang mendasarinya saat dijalankan.
  4. ExpandoObject menyimpan data yang mendasarinya dalam Kamus, dll. Jika Anda menerapkan DynamicObject, Anda dapat menyimpan data di mana pun dan sesuka Anda. (mis. bagaimana Anda mendapatkan dan mengatur data pada pengiriman sepenuhnya terserah Anda).

Singkatnya, gunakan DynamicObject ketika Anda ingin membuat tipe SENDIRI yang dapat digunakan dengan DLR dan bekerja dengan perilaku CUSTOM apa pun yang Anda inginkan.

Contoh: Bayangkan bahwa Anda ingin memiliki tipe dinamis yang mengembalikan default khusus setiap kali mencoba anggota yang TIDAK ada (yaitu belum ditambahkan pada waktu berjalan). Dan default itu akan berkata, "Maaf, tidak ada cookie di toples ini!". Jika Anda menginginkan objek dinamis yang berperilaku seperti ini, Anda harus mengontrol apa yang terjadi ketika bidang tidak ditemukan. ExpandoObject tidak akan membiarkan Anda melakukan ini. Jadi, Anda harus membuat tipe Anda sendiri dengan perilaku resolusi (pengiriman) anggota dinamis yang unik dan menggunakannya daripada yang sudah jadi ExpandoObject.

Anda dapat membuat jenis sebagai berikut: (Catatan, kode di bawah ini hanya untuk ilustrasi dan mungkin tidak berjalan. Untuk mempelajari tentang cara menggunakan DynamicObject dengan benar, ada banyak artikel dan tutorial di tempat lain.)

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

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties.ContainsKey(binder.Name))
        {
            result = properties[binder.Name];
            return true;
        }
        else
        {
            result = "I'm sorry, there are no cookies in this jar!"; //<-- THIS IS OUR 
            CUSTOM "NO COOKIES IN THE JAR" RESPONSE FROM OUR DYNAMIC TYPE WHEN AN UNKNOWN FIELD IS ACCESSED
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        dynamic method = properties[binder.Name];
        result = method(args[0].ToString(), args[1].ToString());
        return true;
    }
}

Sekarang, kita bisa menggunakan kelas imajiner yang baru saja kita buat sebagai tipe dinamis yang memiliki perilaku sangat khusus jika bidang tidak ada.

dynamic d = new MyNoCookiesInTheJarDynamicObject();
var s = d.FieldThatDoesntExist;

//in our contrived example, the below should evaluate to true
Assert.IsTrue(s == "I'm sorry, there are no cookies in this jar!")

ExpandoObjectadalah implementasi LENGKAP dari IDynamicMetaObjectProvider, di mana tim .NET Framework telah membuat semua keputusan ini untuk Anda. Ini berguna jika Anda tidak memerlukan perilaku khusus, dan Anda merasa bahwa ExpandoObject bekerja cukup baik untuk Anda (90% dari waktu, ExpandoObjectcukup baik). Jadi misalnya, lihat yang berikut ini, dan untuk ExpandoObject, desainer memilih untuk melemparkan pengecualian jika anggota dinamis tidak ada.

dynamic d = new ExpandoObject();

/*
The ExpandoObject designers chose that this operation should result in an 
Exception. They did not have to make that choice, null could 
have been returned, for example; or the designers could've returned a "sorry no cookies in the jar" response like in our custom class. However, if you choose to use 
ExpandoObject, you have chosen to go with their particular implementation 
of DynamicObject behavior.
*/

try {
var s = d.FieldThatDoesntExist;
}
catch(RuntimeBinderException) { ... }

Jadi untuk meringkas, ExpandoObjecthanyalah satu cara yang dipilih sebelumnya untuk memperluas DynamicObject dengan perilaku pengiriman dinamis tertentu yang mungkin akan bekerja untuk Anda , tetapi mungkin tidak tergantung pada kebutuhan khusus Anda.

Sedangkan, DyanmicObjectadalah tipe dasar pembantu yang membuat menerapkan tipe Anda sendiri dengan perilaku dinamis yang unik sederhana dan mudah.

Tutorial bermanfaat yang menjadi dasar dari banyak contoh sumber di atas.

Ayo saya
sumber
Penjelasan yang sangat bagus. Hanya satu koreksi teknis: ExpandoObject tidak mewarisi dari DynamicObject.
Mike Rosoft
Koreksi kecil pada contoh untuk DynamicObject: ketika menimpa TryGetMember, jika Anda mengembalikan false, RuntimeBinderExceptionakan dilemparkan ketika mencoba mengakses ke properti yang tidak ada. Agar snipet benar-benar berfungsi, Anda harus kembali true.
lluchmk
36

Menurut spesifikasi bahasa C # dynamicadalah deklarasi jenis. Yaitu dynamic xberarti variabel xmemiliki tipedynamic .

DynamicObject adalah tipe yang membuatnya mudah diimplementasikan IDynamicMetaObjectProvider dan dengan demikian mengesampingkan perilaku mengikat spesifik untuk tipe tersebut.

ExpandoObjectadalah jenis yang bertindak seperti tas properti. Yaitu Anda dapat menambahkan properti, metode, dan sebagainya ke contoh dinamis dari tipe ini saat runtime.

Brian Rasmussen
sumber
25
dynamicbukan tipe aktual ... itu hanya sebuah petunjuk untuk memberitahu kompiler untuk menggunakan pengikatan akhir untuk variabel ini. dynamicvariabel sebenarnya dideklarasikan seperti objectdalam MSIL
Thomas Levesque
1
@ Thomas: dari sudut pandang kompiler itu adalah tipe, tapi Anda benar bahwa representasi runtime adalah dari Object. Anda akan menemukan pernyataan "diketik secara statis menjadi dinamis" di beberapa presentasi MS.
Brian Rasmussen
3
@ Thomas: dan spec bahasa mengatakan "C # 4.0 memperkenalkan tipe statis baru yang disebut dinamis".
Brian Rasmussen
memang ... Tapi saya pikir itu membingungkan untuk menganggapnya sebagai tipe, karena tidak ada hubungan warisan dengan tipe seperti DynamicObject atau ExpandoObject
Thomas Levesque
3
@NathanA aku bersamamu di sini. Namun, spesifikasi bahasa menyebutnya tipe, jadi itulah yang akan saya lakukan.
Brian Rasmussen
0

Contoh di atas DynamicObjecttidak memberi tahu perbedaannya, karena pada dasarnya menerapkan fungsi yang sudah disediakan olehExpandoObject .

Dalam dua tautan yang disebutkan di bawah ini, sangat jelas bahwa dengan bantuan DynamicObject , dimungkinkan untuk melestarikan / mengubah tipe aktual ( XElementdalam contoh yang digunakan dalam tautan di bawah) dan kontrol yang lebih baik pada properti dan metode.

https://blogs.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-introducing-the-expandoobject/

https://blogs.msdn.microsoft.com/csharpfaq/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject/

public class DynamicXMLNode : DynamicObject    
{    
    XElement node;

    public DynamicXMLNode(XElement node)    
    {    
        this.node = node;    
    }

    public DynamicXMLNode()    
    {    
    }

    public DynamicXMLNode(String name)    
    {    
        node = new XElement(name);    
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)    
    {    
        XElement setNode = node.Element(binder.Name);

        if (setNode != null)    
            setNode.SetValue(value);    
        else    
        {    
            if (value.GetType() == typeof(DynamicXMLNode))    
                node.Add(new XElement(binder.Name));    
            else    
                node.Add(new XElement(binder.Name, value));    
        }

        return true;    
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)    
    {    
        XElement getNode = node.Element(binder.Name);

        if (getNode != null)    
        {    
            result = new DynamicXMLNode(getNode);    
            return true;    
        }    
        else    
        {    
            result = null;    
            return false;    
        }    
    }    
}
Deepak Mishra
sumber