前一篇我們說明了使用 ExpandoObject 輕易自製出動態物件的能力,光是使用 ExpandoObject 就能滿足我們大多數的需求,不過若是想要進一步的深入到動態語言的機制來建立動態物件的話,那麼我們可以利用 DynamicObject 物件來實作。
前一篇我們說明了使用 ExpandoObject 輕易自製出動態物件的能力,光是使用 ExpandoObject 就能滿足我們大多數的需求,不過若是想要進一步的深入到動態語言的機制來建立動態物件的話,那麼我們可以利用 DynamicObject 物件來實作。
DynamicObject 是一個簡單的動態物件的基礎類別,它不是抽象類別,但你也沒辦法直接使用它,因為它和 DLR 機制結合在一起,在沒有為它加油添醋時,它是沒辦法使用的,我們可以繼承它來取得它的功能,不過要如何啟用它的功能呢?你可以下 override 指令,然後會看到一堆以 Try 開頭的函式:
是的,要賦與它功能,就要對這些函式進行覆寫,來加入自己要的功能。
以前一篇的例子而言,我們要給予存取屬性的能力以及呼叫方法的能力,所以我們一共要覆寫:
- TryGetMember
- TrySetMember
例如下列實作碼:
public class MyDynamicObject : DynamicObject
{
private IDictionary<string, object> _objectMembers = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!this._objectMembers.ContainsKey(binder.Name))
{
result = null;
return false;
}
else
{
result = this._objectMembers[binder.Name];
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!this._objectMembers.ContainsKey(binder.Name))
{
this._objectMembers.Add(binder.Name, value);
return true;
}
else
{
this._objectMembers[binder.Name] = value;
return true;
}
}
}
這裡我們使用了 IDictionary<string, object> 來容納我們將加入的成員,TryGetMember() 和 TrySetMember() 會允許我們自訂加入成員的邏輯,若不想讓它成功時,只要傳回 false,DLR 就會自動幫我們產生 RuntimeBinderException 例外,表示成員的存取失敗。所以我們可以更進一步的控制成員的存取規則,而不用受限於 IDictionary<string, object> 的規則,只是我們為了簡單化,所以在這裡只做了是否有成員名稱的判斷。
主程式和前一篇一樣沒什麼變:
static void Main(string[] args)
{
dynamic d = CreateDynamicObject();
Console.WriteLine("Prop1: {0}", d.Prop1);
Console.WriteLine("Prop2: {0}", d.Prop2);
Console.WriteLine("Prop3: {0}", d.Prop3);
d.InvokeVoid();
var e = d.InvokeAdd(1, 2);
Console.WriteLine("e={0}", e);
Console.Read();
}
static dynamic CreateDynamicObject()
{
dynamic d = new MyDynamicObject();
// assign property
d.Prop1 = 1;
d.Prop2 = 2;
d.Prop3 = 3;
// assign void method.
d.InvokeVoid = new Action(() => Console.WriteLine("InvokeVoid."));
// assign returnable method.
d.InvokeAdd = new Func<int, int, int>((a, b) => a + b);
return d;
}
你可以試著執行看看,能得到和前一篇一樣的效果。
Sample Code: https://gist.github.com/regionbbs/8179211