今天看里面的dynamic用法,我犹豫从来没接触过,今天恶补了一下,把我对dynamic的认识分享了出来,大家一起学习。

Visual C# 2010 引入了一个新类型 dynamic。

该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查。

大多数情况下,该对象就像具有类型 object 一样。 在编译时,将假定类型化为 dynamic 的元素支持任何操作。

为什么会有这个类呢,我们可以看一下js的代码来判断为什么会需要这个类

function () { 
varnew Object(); 
 
function"我Sayhi方法"); } 
varnew Object(); 
 
//我是SayHi方法
//没有此方法 
 } 
function IsMetheod(obj) 
 { 
iftypeof"function") { 
return obj.SayHi(); 
 } 
else { 
"没有此方法"); 
returnnull; 
 } 
 }

 

通过js代码我们可以去直接在对象上添加属性,而且还可以在使用属性之前去判断该属性或者方法是否存在。如果想要这样做,我们可以直接去操作dynamic对象去操作。

首先我们应先了解到一个接口,虽然dynamic可以像object类一样标识大部分类,但是如果想要用到像下面的例子一样动态的为对象绑定属性,必须实现IDynamicMetaObjectProvider这个接口。(这个接口我们先看完这个例子再说)

dynamicnewExpandoObject(); 
".NET"; 
 dy.age = 5; 
 
Console.WriteLine(dy.name+":"+dy.age);//".NET:5"

大家可能看到了我们new的不是dynamic,对的,应为dynamic是一个静态的类型,所以我们是没有办法去new的,那怎样得到呢,这就提到了IDynamicMetaObjectProvider这个接口了,至于这个接口有什么用,微软官网上是这么说的"表示一个动态对象,在运行时可将该对象的操作进行绑定。"这也就是说,我们在程序编译之后运行的时候,把给对象绑定的方法动态的加载进去,所以,我们为dynamic对象绑定属性的时候,编译是能通过的,只是在运行的时候会出错。

这个接口里面只有一个方法GetMetaObject(),这个方法的作用是返回 DynamicMetaObject,它负责绑定对此对象执行的操作。所以只要实现了这个接口,我们就可以对实现该接口的类执行动态绑定操作。上例中也正是因为ExpanoObject实现了这个接口,可以通过new 这个类来演示动态绑定对象的方法。

DynamicObject这个类,我们可以通过重写里面的一些方法来扩展属性。下图是C#定义的DynamicObject

 

好了,说了这么多铺垫,我们来真正看一下比较简单的应用吧,我们以前写c#代码是不能通过js去那样写的的,让我们来看看通过dynamic我们是否可以完成这样的写法呢?

注意:下面的方法都是DynamicObject定义的,我们重写就好了,不必纠结为什么这样,看完再说。

publicclassMydynamic:DynamicObject
 { 
//转杯一个数据字典,去保存我们属性和属性值
Dictionary<string, object> dic = newDictionary<string, object>(); 
 
publicoverrideIEnumerable<string> GetDynamicMemberNames() 
 { 
return dic.Keys; 
 } 
//设置值的属性
publicoverrideboolSetMemberBinderobject value) 
 { 
 dic[binder.Name] = value; 
returntrue; 
 } 
 
//得到值
publicoverrideboolGetMemberBinderoutobject result) 
 { 
 
out result); 
returntrue; 
 } 
 
classProgram
 { 
staticvoidstring[] args) 
 { 
dynamicnewMydynamic(); 
"小白"; 
 dy.age = 15; 
Console.WriteLine(dy.Name+":"+dy.age);//"小白:15"
Console.ReadKey(); 
 } 
}

 

看到了吧,我们通过继承的DynamicObject这个类同样可以实现对对象属性的动态添加,不过要注意这里面的两个方法,就是TryGetMenber和TryGetMember这两个方法,没错这两个方法就是为为动态属性赋值和取值的,此外还有很多删除、通过下标得到属性等方法。设置值和取值的方法给我们了,我们如果想对获取的值进行业务的处理,在这里完全可以做得到。我们还是来现分析一下,这个主程序是怎么工作的,当dy.Name的时候,先从该类的集合字典里面去找,如果找到这个属性,为其赋值或者修改,如果没有找到,则添加到数据字典里。在运行的时候,绑定到这个对象上,该对象就有了该属性了。

我们再看一下一个判断业务的例子吧,

假定您需要一种数据结构来存储数字的文本和数值表达式,并且您希望定义此数据结构到字符串和整数的转换。

下面的代码示例演示如何实现从 DynamicObject 类派生的 DynamicNumberDynamicNumber 重写 TryConvert 方法来启用类型转换。 它还会覆盖 TrySetMember 和TryGetMember 方法以允许访问数据元素。

在此示例中,仅支持到字符串和整数的转换。 如果试图将对象转换为任何其他类型,将引发运行时异常。

publicclassDemo_dynymic:System.Dynamic.DynamicObject
 { 
 
Dictionary<string, object> dictionary 
newDictionary<string, object>(); 
 
 
publicoverridebool TryGetMember( 
GetMemberBinderoutobject result) 
 { 
returnout result); 
 } 
 
 
publicoverridebool TrySetMember( 
SetMemberBinderobject value) 
 { 
 dictionary[binder.Name] = value; 
returntrue; 
 } 
 //这个方法是用类型转换的。 
publicoverridebool TryConvert( 
ConvertBinderoutobject result) 
 { 
iftypeof(String)) 
 { 
"Txt"]; 
returntrue; 
 } 
iftypeof(int)) 
 { 
"Num"]; 
returntrue; 
 } 
returnbase.TryConvert(binder, out result); 
 } 
} 
 
 
 
classProgram
 { 
staticvoidstring[] args) 
 { 
dynamicnewDemo_dynymic(); 
"哈哈哈"; 
 dy.Num = 1; 
int num = dy;//会启用TryConvert 
string txt = dy;//会启用TryConvert 
Console.WriteLine(num+txt);//1哈哈哈
Console.ReadKey(); 
 } 
 }

 

我们再来看看里面的TryInvoke这个方法的用法吧。

TryInvoke从 DynamicObject 类派生的类可以重写此方法,以指定如何为动态对象执行调用对象的操作。 未重写此方法时,语言的运行时联编程序确定该行为。(大多数情况下,将引发运行时异常。)

如果此方法被重写,当您执行运算(如 sampleObject(100))时自动调用此方法,其中 sampleObject 从 DynamicObject 类派生。说简单点,就是我们可以通过重写这个方法,使用函数的形式来改变类对象的行为,也就是说我们通过函数形式来操作该类的属性。举个例子。

我们不想通过类名.属性的方式来添加属性,我们可以改写TryInvoke方法来完成通过函数参数形式赋值。具体看代码。

publicoverridebool TryInvoke( 
InvokeBinderobject[] args, outobject result) 
 { 
//有两个参数,而且第一个参数是整型,第二个是字符型。
//因为我们通过这种形式赋值,所以在用的时候,数据类型一定要一致。
if ((args.Length == 2) && 
typeof(int)) && 
typeof(String))) 
 { 
if"Num")) 
"Num"] = args[0]; 
else
"Num", args[0]); 
if"Txt")) 
"Txt"] = args[1]; 
else
"Txt", args[1]); 
 
true; 
returntrue; 
 } 
else
 { 
false; 
returnfalse; 
 } 
 }
classProgram
 { 
staticvoidstring[] args) 
 { 
dynamicnewDemo_dynymic(); 
//通过方法对属性赋值 
"one"); 
//因为属性名在后台我们已经确定了,所以访问的时候要写出来
//注意:.不出来,因为编译的时候,属性还不存在,只有运行的时候,才存在
Console.WriteLine(dy.Txt+dy.Num);//one1
Console.ReadKey(); 
 } 
}

 

还有很多方法,我就不一一例举了,总之要实现对对象的动态属性添加,有几个步骤

 1. 子类里面包含一个私有变量,用于存储数据. 这暂且叫做Data;

 2.TryGetMember(GetMemberBinder binder, out object result) 方法实现对数据的获取. binder.Name就是需要获取的属性的名称,result 是获取的属性值. 通过binder.Name在Data中获取到对应的属性值,传出到外面.(注意到了吧result是out参数)

3.TrySetMember(SetMemberBinder binder, object value) 对存在的属性进行赋值. 上面的Set方法中,我都判断了binder.Name在data里面是否存在。如果不存在就无法赋值。返回false,如果外面对不存在的属性复制那么将会报错.。