ExpandoObject, DynamicObject, DynamicMetaObject

ExpandoObject

由于ExpandObject的先天不足(无特征性):

1. ExpandObject不能用于太复杂的对象。

ExpandObject最好还是作为简单的数据容器,不要弄得过于复杂,甚至包含有函数处理。

2.ExpandObject的使用范围必须要短

范围短的意思是,产生和使用ExpandObject的代码的路径必须要短(主要是函数调用路径)。如果你正在使用一个ExpandObject对象,查看产生这个ExpandObject的地方,发现分散在好几个函数之中,还有嵌套的话,那么这个ExpandObject是非常难于维护的。

3. ExpandObject的使用场合最好贴近程序的终端。

比如在MVC中的ViewBag, 就是一个好的例子。ViawBag用于生成页面, 而页面就是MVC程序的终端了。到了终端,ExpandObject也就不能*它人了。
正是由于ExpandObject的无特征性,什么都可以做,所以容易导致滥用。

    static void Main(string[] args)
    {
        Console.WriteLine("-------1.简便存储-------");
        {
            dynamic expando = new ExpandoObject();
            expando.First = "value set dynamically";
            expando.Second = 2;
            Console.WriteLine(expando.First);
            Console.WriteLine(expando.Second);
        }
//-------1.简便存储-------
//value set dynamically
//2
        
        Console.WriteLine("-------2.用字典方式存储-------");
        {
            dynamic expando = new ExpandoObject();
            IDictionary<string, object> dictionary = expando;
            expando.First = "value set dynamically";
            Console.WriteLine(dictionary["First"]);

            dictionary["Second"] = "value set with dictionary";
            Console.WriteLine(expando.Second);
        }
//-------2.用字典方式存储-------
//value set dynamically
//value set with dictionary
        
        Console.WriteLine("-------3.用委托伪造ExpandoObject的方法-------");
        {
            //Func<int, int> result = x => x + 1;
            dynamic expando = new ExpandoObject();
            expando.AddOne = (Func<int, int>)(x => x + 1);
            Console.Write(expando.AddOne(10));

        }
        Console.Read();

    }
//-------3.用委托伪造ExpandoObject的方法-------
//11

DynamicObject

DynamicObject有个构造函数,但是protected, 也就是我们没有办法直接实例化来使用它。只能是通过继承来构造DynamicObject的对象。

同时DynamicObject中很很多标记为Virtual的方法,比如:

public virtual bool TryGetMember(GetMemberBinder binder, out object result);

public virtual bool TrySetMember(SetMemberBinder binder, object value);
ExpandoObject, DynamicObject, DynamicMetaObject

案例1(错误示范):

class DynamicProduct : DynamicObject
{
    public string name;
    public int Id { get; set; }

    public void ShowProduct()
    {
        Console.WriteLine("Id={0} ,Name={1}", Id, name);
    }

    #region Override DynamicObject 的方法

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return base.GetDynamicMemberNames();
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        Console.WriteLine("TryGetMember被调用了,Name:{0}", binder.Name);
        return base.TryGetMember(binder, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        Console.WriteLine("TrySetMember被调用了,Name:{0}", binder.Name);
        return base.TrySetMember(binder, value);
    }

    public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
    {
        Console.WriteLine("TryInvoke被调用了");
        return base.TryInvoke(binder, args, out result);
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        Console.WriteLine("TryInvokeMember被调用了,Name:{0}", binder.Name);
        return base.TryInvokeMember(binder, args, out result);
    }

    #endregion
}
static void Main(string[] args)
{
    dynamic dynProduct = new DynamicProduct();

    dynProduct.name = "n1"; //调用TrySetMember方法
    dynProduct.Id = 1;
    dynProduct.Id = dynProduct.Id + 3;
    dynProduct.ShowProduct();

    Console.ReadLine();
}

//-------DynamicObject-------
//Id=4 ,Name=n1
//n1

没有执行TryXXX....

将DynamicProduct 中的name修饰符改为private:

private string name;

可以在TrySetMember方法中设置断点,再次运行:

ExpandoObject, DynamicObject, DynamicMetaObject

ExpandoObject, DynamicObject, DynamicMetaObject

ExpandoObject, DynamicObject, DynamicMetaObject

为什么访问修饰符是Public不调用TrySetMember,是Private 就调用了呢??

难道是因为private抛出了异常吗??

再次看看Msdn对此的TrySetMember方法的解释:

Msdn****备注

…………….动态语言运行库 (DLR) 将首先使用语言联编程序在类中查找属性的静态定义。 如果没有此类属性,****DLR 调用 TrySetMember 方法。

问题的原因是这样的:首先DLR 使用语言联编程序在类中查找name的静态定义,

因为name是public,所以查找到了,然后返回,不会去调用TrySetMember方法了,

但是如果name是private,那么联编程序在类中没找到name的静态定义,于是DLR尝试调用TrySetMember方法。

案例2:(正确示范)

 public class DynamicProduct : DynamicObject
{
    Dictionary<string, Object> _dic = new Dictionary<string, object>();

    //设置为 public ,赋值时,就不会访问 TrySetMember
    //private object name { get; set; }
    //public int Id { get; set; }

    //public void ShowProduct()
    //{
    //    Console.WriteLine("Id={0} ,Name={1}", Id, name);
    //}

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        Console.WriteLine("TryGetMember被调用了,Name:{0}", binder.Name);
        var name = binder.Name;
        return _dic.TryGetValue(name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        Console.WriteLine("TrySetMember被调用了,Name:{0}", binder.Name);
        _dic[binder.Name] = value;
        return true;
    }
}
static void Main(string[] args)
{
     dynamic dynProduct = new DynamicProduct();

    dynProduct.name = "n1"; //调用TrySetMember方法
    //dynProduct.Id = 1;
    //dynProduct.Id = dynProduct.Id + 3;
    //dynProduct.ShowProduct();

    Console.WriteLine(dynProduct.name);

    Console.ReadLine();
}

//output:
//TrySetMember被调用了,Name:name
//TryGetMember被调用了,Name:name
//n1

DynamicMetaObject

未完...

参考:

理解C# 4 dynamic(3) – DynamicObject的使用

浅谈Dynamic 关键字系列之三(下):ExpandoObject,DynamicObject,DynamicMetaObject

上一篇:Service有几种启动方式?吐血整理


下一篇:如何在 Spring 生态中玩转 RocketMQ?