[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