[C#]簡介ExpandoObject

ExpandoObject 是 .Net Framework 4 之後出現的一個類別,可以幫助我們為物件動態的加入或移除屬性成員,今天就來聊聊 ExpandoObject 的特性及用法。

會注意到 ExpandoObject 其實只是同事間討論時聊到,得知類似 dynamic,不一樣的是 dynamic 只是編譯時期不做物件是否具有屬性的檢查,改在執行時期才處理,但卻不能動態增加屬性(因為接收的還是固定的那個物件);而 ExpandoObject 則可以動態的替物件增加或移除屬性,使用上彈性比較大。

原本覺得可以用 HashTable 做就好了,反正在寫程式碼時無論如何都不會在編譯時期檢查屬性(或是key)的存在,實際看過MSDN上的ExpandoObject定義後,才發現雖不中亦不遠矣!MSDN上對ExpandoObject的語法定義如下:

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

可以看到 ExpandoObject 其實就是實作了 IDictionary<string, object>,但在使用上會比 Dictionary<string, object> 簡單,另外也實作了 INotifyPropertyChanged,在屬性變更時可以更明確地掌握狀態!

不過要注意的是 ExpandoObject 雖然方便強大,但還是需要配合 dynamic 語法,才能發揮出它的威力!還有就是使用 dynamic 會變成"類似弱型別"的寫法,不小心就會造成執行時期的錯誤,不得不防!

使用 Dictionary<string, object>

先看看使用 Dictionary<string, object> 看起來是什麼樣子?

var data = new Dictionary<string, object>();
data.Add("Name", "Wellwind");
data.Add("Age", 30);
data["Sex"] = Sex.Male;

Console.WriteLine("-- Dictionary --");
Console.WriteLine(String.Format("Name={0}", data["Name"]));
Console.WriteLine(String.Format("Age={0}", data["Age"]));
Console.WriteLine(String.Format("Sex={0}", data["Sex"]));

執行結果如下:

基本上沒有什麼問題,就是看到一堆key字串比較不舒服而已,ExpandoObject 正好可以幫我們解決這個問題!

使用ExpandoObject

基本用法

透過ExpandoObject,我們可以不必把字串當作key值,而是直接當物件屬性來使用

dynamic data = new ExpandoObject();
data.Name = "Welwid";
data.Age = 30;
data.Sex = Sex.Male;

Console.WriteLine("-- ExpandoObject --");
Console.WriteLine(String.Format("Name={0}", data.Name));
Console.WriteLine(String.Format("Age={0}", data.Age));
Console.WriteLine(String.Format("Sex={0}", data.Sex));

執行結果如下:

看到差異了嗎?不知道 ExpandoObject 人乍看之下還會以為這段程式碼的ExpandoObject是一個定義好3個屬性的類別,但其實 ExpandoObject 只是幫我們節省 Dictionary 做的事情而已,讓程式碼看起來更舒服!

轉換成 IDictionary

由於 ExpandoObject 本身就實作了 IDictionary<string, object>,因此若是有必要我們也可以直接把它轉型成 IDictionary<string, object> 來操作

dynamic data = new ExpandoObject();
data.Name = "Wellwind";
data.Age = 30;
// 轉型為IDictionary<string, object>進行操作
(data as IDictionary<string, object>)["Sex"] = Sex.Male;

Console.WriteLine("-- ExpandoObject --");
Console.WriteLine(String.Format("Name={0}", data.Name));
Console.WriteLine(String.Format("Age={0}", data.Age));
Console.WriteLine(String.Format("Sex={0}", data.Sex));

PropertyChanged 事件

ExpandoObject也實作了 INotifyPropertyChanged.PropertyChanged 事件,因此我們可以定義 PropertyChanged 事件,來監聽屬性(其實就是 IDictionary)的變化:

dynamic data = new ExpandoObject();

(data as INotifyPropertyChanged).PropertyChanged += (sender, e) =>
{
    Console.WriteLine(String.Format("Property {0} has changed.", e.PropertyName));
};

data.Name = "Wellwind";
...

執行結果如下:

回顧

ExpandoObject 幫助我們簡化了部分情境需要使用 Dictionary 的麻煩,也提供了比較多的擴充,讓我們在使用時更加方便,寫出來的程式碼也比較好理解;在一些物件資料傳遞時,使用 ExpandoObject 也可以減少我們建立 DTO 物件的麻煩,但畢竟不是強型別的資料,在使用上還是需要謹慎對待!

程式碼範例:https://github.com/wellwind/ExpandoObjectDemo

參考資料

如果您覺得我的文章有幫助,歡迎免費成為 LikeCoin 會員,幫我的文章拍手 5 次表示支持!