Apa manfaat sebenarnya dari ExpandoObject?

587

Kelas ExpandoObject yang ditambahkan ke .NET 4 memungkinkan Anda untuk secara sewenang-wenang mengatur properti ke objek saat runtime.

Apakah ada kelebihan untuk ini daripada menggunakan Dictionary<string, object>, atau benar-benar bahkan Hashtable ? Sejauh yang saya tahu, ini hanyalah tabel hash yang dapat Anda akses dengan sintaks yang sedikit lebih ringkas.

Misalnya, mengapa ini:

dynamic obj = new ExpandoObject();
obj.MyInt = 3;
obj.MyString = "Foo";
Console.WriteLine(obj.MyString);

Benar-benar lebih baik, atau sangat berbeda, dari:

var obj = new Dictionary<string, object>();
obj["MyInt"] = 3;
obj["MyString"] = "Foo";

Console.WriteLine(obj["MyString"]);

Apa keuntungan nyata yang diperoleh dengan menggunakan ExpandoObject alih-alih hanya menggunakan jenis kamus arbitrer, selain tidak jelas bahwa Anda menggunakan jenis yang akan ditentukan saat runtime.

Reed Copsey
sumber

Jawaban:

689

Karena saya menulis artikel MSDN yang Anda maksud, saya kira saya harus menjawab yang ini.

Pertama, saya mengantisipasi pertanyaan ini dan itulah sebabnya saya menulis posting blog yang menunjukkan kasus penggunaan yang kurang lebih nyata untuk ExpandoObject: Dynamic di C # 4.0: Memperkenalkan ExpandoObject .

Singkatnya, ExpandoObject dapat membantu Anda membuat objek hierarkis yang kompleks. Misalnya, bayangkan Anda memiliki kamus di dalam kamus:

Dictionary<String, object> dict = new Dictionary<string, object>();
Dictionary<String, object> address = new Dictionary<string,object>();
dict["Address"] = address;
address["State"] = "WA";
Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]);

Semakin dalam adalah hierarki, yang lebih buruk adalah kode. Dengan ExpandoObject tetap elegan dan mudah dibaca.

dynamic expando = new ExpandoObject();
expando.Address = new ExpandoObject();
expando.Address.State = "WA";
Console.WriteLine(expando.Address.State);

Kedua, seperti yang sudah ditunjukkan, ExpandoObject mengimplementasikan antarmuka INotifyPropertyChanged yang memberi Anda lebih banyak kontrol atas properti daripada kamus.

Akhirnya, Anda dapat menambahkan acara ke ExpandoObject seperti di sini:

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

       // Initialize the event to null (meaning no handlers)
       d.MyEvent = null;

       // Add some handlers
       d.MyEvent += new EventHandler(OnMyEvent);
       d.MyEvent += new EventHandler(OnMyEvent2);

       // Fire the event
       EventHandler e = d.MyEvent;

       e?.Invoke(d, new EventArgs());
   }

   static void OnMyEvent(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent fired by: {0}", sender);
   }

   static void OnMyEvent2(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent2 fired by: {0}", sender);
   }
}

Juga, ingatlah bahwa tidak ada yang menghalangi Anda untuk menerima argumen peristiwa secara dinamis. Dengan kata lain, alih-alih menggunakan EventHandler, Anda dapat menggunakan EventHandler<dynamic>yang akan menyebabkan argumen kedua dari pawang menjadi dynamic.

Alexandra Rusina
sumber
53
Menarik. Terima kasih atas info re: events. Itu yang baru bagi saya.
Reed Copsey
16
@AlexandraRusina, bagaimana ia tahu ini peristiwa ketika Anda mengatakan d.MyEvent = null;, Atau tidak?
Shimmy Weitzhandler
20
Mungkin saya kehilangan sesuatu, tapi ini bukan acara - ini adalah properti sederhana tipe delegasi.
Sergey Berezovskiy
7
Blok pertama dapat ditulis menggunakan tipe anonim: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);Saya menemukan ini lebih mudah dibaca tetapi ymmv. Dan mengingat itu diketik secara statis, itu lebih berguna dalam konteks ini.
nawfal
13
@nawfal itu tidak benar - anonim berbeda dengan Expando. Anda membuat jenis anonim, yang kemudian tidak dapat menambahkan properti sewenang-wenang.
Dr Blowhard
75

Satu keuntungan adalah untuk skenario yang mengikat. Kisi data dan kisi properti akan mengambil properti dinamis melalui sistem TypeDescriptor. Selain itu, pengikatan data WPF akan memahami properti dinamis, sehingga kontrol WPF dapat berikatan dengan ExpandoObject lebih mudah daripada kamus.

Interoperabilitas dengan bahasa dinamis, yang akan mengharapkan properti DLR daripada entri kamus, juga dapat menjadi pertimbangan dalam beberapa skenario.

itowlson
sumber
6
Ini tampaknya penyatuan data untuk objek dinamis rusak . Pengguna pelaporan eisenbergeffect ada di sini di SO dan koordinator caliburn.micro. @AlexandraRusina dapatkah Anda mengomentari status bug dan status "Tidak akan diperbaiki"
surfmuggle
2
Bagi mereka yang penasaran, saya saat ini dapat mengikat List<dynamic>dan IEnumerable<dynamic>menggunakan WPF4
Graham Bass
47

Manfaat nyata bagi saya adalah pengikatan data yang benar-benar mudah dari XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

 <TextBlock Text="{Binding SomeData.WhatEver}" />
bjull
sumber
28

Interop dengan bahasa lain yang ditemukan pada DLRalasan # 1 itu bisa saya pikirkan. Anda tidak dapat melewati mereka Dictionary<string, object>karena ini bukan IDynamicMetaObjectProvider. Manfaat tambahan lainnya adalah mengimplementasikannya INotifyPropertyChangedyang berarti dalam dunia penyatuan data WPF, ia juga memiliki manfaat tambahan di luar apa yang Dictionary<K,V>dapat Anda berikan.

Drew Marsh
sumber
19

Ini semua tentang kenyamanan programmer. Saya bisa membayangkan menulis program cepat dan kotor dengan objek ini.

Kekacauan Kekacauan
sumber
9
@J. Hendrix, jangan lupa bahwa dia juga berkata "kotor". Intellisense memiliki kekurangannya, namun, membuatnya lebih mudah untuk debugging dan menangkap bug. Saya pribadi masih lebih suka tipe statis daripada dinamis kecuali saya berurusan dengan kasus aneh (dan selalu langka).
Phil
+1 untuk kenyamanan. Namun saya menemukan jenis anonim dapat sama nyamannya dengan tas properti sederhana dan hanya lebih baik untuk ke-statis-annya.
nawfal
1
Saya tidak ingin menggunakannya dalam kode produksi, tetapi sangat nyaman dalam uji-kode dan dapat membuatnya terlihat sangat indah.
Tobias
14

Saya pikir ini akan memiliki manfaat sintaksis, karena Anda tidak akan lagi "memalsukan" properti yang ditambahkan secara dinamis dengan menggunakan kamus.

Itu, dan interop dengan bahasa dinamis saya akan berpikir.

gn22
sumber
11

Ini adalah contoh dari artikel MSDN yang hebat tentang penggunaan ExpandoObject untuk membuat tipe ad-hoc dinamis untuk data terstruktur yang masuk (yaitu XML, Json).

Kami juga dapat menetapkan delegasi ke properti dinamis ExpandoObject :

dynamic person = new ExpandoObject();
person.FirstName = "Dino";
person.LastName = "Esposito";

person.GetFullName = (Func<String>)(() => { 
  return String.Format("{0}, {1}", 
    person.LastName, person.FirstName); 
});

var name = person.GetFullName();
Console.WriteLine(name);

Dengan demikian memungkinkan kita untuk menyuntikkan beberapa logika ke objek dinamis saat runtime. Oleh karena itu, bersama dengan ekspresi lambda, penutupan, kata kunci dinamis, dan kelas DynamicObject , kami dapat memperkenalkan beberapa elemen pemrograman fungsional ke dalam kode C # kami, yang kami tahu dari bahasa dinamis seperti JavaScript atau PHP.

sgnsajgon
sumber
4

Ada beberapa kasus di mana ini berguna. Saya akan menggunakannya untuk shell Modularized misalnya. Setiap modul mendefinisikan Dialog Konfigurasi itu sendiri yang didasari oleh pengaturannya. Saya memberinya ExpandoObject karena Datacontext dan menyimpan nilai-nilai dalam Storage konfigurasi saya. Dengan cara ini penulis Dialog Konfigurasi hanya harus Mengikat ke Nilai dan secara otomatis dibuat dan disimpan. (Dan disediakan untuk modul untuk menggunakan pengaturan ini tentu saja)

Ini lebih mudah digunakan daripada Kamus. Tetapi semua orang harus sadar bahwa secara internal itu hanya sebuah Kamus.

Ini seperti LINQ hanya gula sintaksis, tetapi kadang-kadang membuatnya lebih mudah.

Jadi untuk menjawab pertanyaan Anda secara langsung: Lebih mudah untuk menulis dan lebih mudah dibaca. Tetapi secara teknis itu pada dasarnya adalah Dictionary<string,object>(Anda bahkan dapat memasukkannya ke dalam daftar nilai).

n1LWeb
sumber
-1
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

Saya pikir itu hanya berfungsi karena semuanya memiliki ToString (), jika tidak, Anda harus tahu tipe itu dan melemparkan 'objek' ke tipe itu.


Beberapa di antaranya bermanfaat lebih sering daripada yang lain, saya mencoba untuk teliti.

  1. Mungkin jauh lebih alami untuk mengakses koleksi, dalam hal ini apa yang secara efektif "kamus", menggunakan notasi titik lebih langsung.

  2. Sepertinya ini bisa digunakan sebagai Tuple yang sangat bagus. Anda masih dapat memanggil anggota Anda "Item1", "Item2" dll ... tetapi sekarang Anda tidak harus, itu juga bisa berubah, tidak seperti Tuple. Ini memang memiliki kelemahan besar dari kurangnya dukungan intellisense.

  3. Anda mungkin merasa tidak nyaman dengan "nama anggota sebagai string", seperti halnya merasakan dengan kamus, Anda mungkin merasa itu terlalu seperti "mengeksekusi string", dan itu dapat menyebabkan konvensi penamaan dikodekan, dan berurusan dengan bekerja dengan morfem dan suku kata ketika kode mencoba memahami cara menggunakan anggota :-P

  4. Bisakah Anda memberikan nilai pada ExpandoObject itu sendiri atau hanya anggotanya? Bandingkan dan kontras dengan dinamis / dinamis [], gunakan mana yang paling sesuai dengan kebutuhan Anda.

  5. Saya tidak berpikir dynamic / dynamic [] bekerja dalam loop foreach, Anda harus menggunakan var, tetapi mungkin Anda dapat menggunakan ExpandoObject.

  6. Anda tidak dapat menggunakan dinamis sebagai anggota data di kelas, mungkin karena itu setidaknya semacam kata kunci, semoga Anda bisa dengan ExpandoObject.

  7. Saya berharap itu "adalah" sebuah ExpandoObject, mungkin berguna untuk melabeli hal-hal yang sangat umum, dengan kode yang membedakan berdasarkan jenis di mana ada banyak hal dinamis yang digunakan.


Bersikap baik jika Anda bisa menelusuri beberapa level sekaligus.

var e = new ExpandoObject();
e.position.x = 5;
etc...

Itu bukan contoh terbaik, bayangkan penggunaan elegan yang sesuai dalam proyek Anda sendiri.

Sayang sekali Anda tidak dapat membuat kode ini dan mendorong hasilnya ke intellisense. Saya tidak yakin bagaimana ini akan bekerja.

Bersikap baik jika mereka dapat memiliki nilai serta anggota.

var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...
juga di sini
sumber
-3

Setelah valueTuples, apa gunanya kelas ExpandoObject? kode 6 baris ini dengan ExpandoObject:

dynamic T = new ExpandoObject();
T.x = 1;
T.y = 2;
T.z = new ExpandoObject();
T.z.a = 3;
T.b= 4;

dapat ditulis dalam satu baris dengan tuple:

var T = (x: 1, y: 2, z: (a: 3, b: 4));

selain itu dengan sintaks tuple Anda memiliki inferensi tipe yang kuat dan dukungan intlisense

Eng M. Hamdy
sumber
1
Contoh Anda tidak identik dalam arti bahwa dengan nilai tuple, Anda tidak dapat menulis Tc = 5; setelah selesai mendefinisikan T. Dengan ExpandoObject Anda dapat melakukannya karena itu dinamis. Contoh Anda dengan nilai tuple sangat identik dengan mendeklarasikan tipe anonim. Misalnya: var T2 = baru {x = 1, y = 2, z = baru {a = 3, b = 4}};
LxL
Mengapa saya harus menulis Tc = 5 tanpa mendefinisikannya? ExpandoObject hanya berguna ketika berhadapan dengan objek COM yang tidak defiend di .net. Kalau tidak, saya tidak akan menggunakan ExpandoObject ini, karena kotor dan bermasalah dalam waktu desain dan runtime.
Eng. M. Hamdy
1
Bagaimana jika Anda memiliki z pada awalnya ditugaskan ke (a: 3, b: 4) dan kemudian Anda ingin z untuk memiliki properti c tambahan? Bisakah Anda melakukannya dengan tuple nilai?
LxL
Jadi poin saya adalah bahwa Anda tidak dapat membandingkan ExpandoObject dengan nilai tuple karena mereka dirancang untuk tujuan yang berbeda. Dengan membandingkan cara Anda, Anda mengabaikan fungsi yang dirancang untuk ExpandoObject, yang merupakan struktur dinamis.
LxL