[.NET] 動態語言能力:自製 dynamic 物件 (2): 使用 DynamicObject 物件

前一篇我們說明了使用 ExpandoObject 輕易自製出動態物件的能力,光是使用 ExpandoObject 就能滿足我們大多數的需求,不過若是想要進一步的深入到動態語言的機制來建立動態物件的話,那麼我們可以利用 DynamicObject 物件來實作。

前一篇我們說明了使用 ExpandoObject 輕易自製出動態物件的能力,光是使用 ExpandoObject 就能滿足我們大多數的需求,不過若是想要進一步的深入到動態語言的機制來建立動態物件的話,那麼我們可以利用 DynamicObject 物件來實作。

DynamicObject 是一個簡單的動態物件的基礎類別,它不是抽象類別,但你也沒辦法直接使用它,因為它和 DLR 機制結合在一起,在沒有為它加油添醋時,它是沒辦法使用的,我們可以繼承它來取得它的功能,不過要如何啟用它的功能呢?你可以下 override 指令,然後會看到一堆以 Try 開頭的函式:

image

是的,要賦與它功能,就要對這些函式進行覆寫,來加入自己要的功能。

以前一篇的例子而言,我們要給予存取屬性的能力以及呼叫方法的能力,所以我們一共要覆寫:

  • 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