dynamic介绍:
前言:
最近公司某项目中类型定义不能在编译期间确定,表结构为动态可变类型。有参考C#4.0最新特性,故成此作。
1. dynamic和var
dynamic是C#4.0新增关键字,和以前动态确定类型的关键字var还是有所不同,以下是区别
dynamic |
var |
|
类型确定时机 |
可推迟到运行期确定数据类型 |
在编译期必须确定数据类型 |
类型改变与否 |
同一dynamic参数可以,针对不同的类型 |
编译期一旦确定了参数类型,则不能改变了 |
函数声明 |
函数声明时参数dynamic是可用的 |
函数声明参数不能使用var |
总结:var类型修饰的参数需要能在编译时确定参数类型,而dynamic可推迟到运行时确定;如果var不能确定类型则编译错误,若dynamic不能确定类型则抛出RuntimeBinderException
2. dynamic和class
一般地,实例化的class可以用dynamic修饰,比如:
class ClassA { public int Property1{get;set;} public int Property2{get;set;} public int Method1(){…} public static int Static_Method1(){…}; } dynamic d=new ClassA(); d.Property1=23; d.Property2=@”Hello, Word!”; d.Method1();
static class ExpandMethod { public static int Add_ClassA(this ClassA a){…} }
3. dynamic和类属性方法
没有的属性和方法是不能调用的。d.Property3=30, d.Method2()这样的代码虽说在编译器不会报警,但是在运行期会有RuntimeBinderException异常。
若不能确定类方法属性,则需要ExpandoObject类,下面是示例:
dynamic expando=new ExpandoObject(); expando.Property1=true; expando.Property2=23; expando.Property3=@”Hello Expando!”; delegate int int_add(int a, int b); expando.Method1=new int_add((a,b)=>(a+b)); expando.Property4=expando.Method1(12,34);
ExpandoObject里面有一个类似Dictionary<string, object>的结构(确切的说有2个列表,一个保存key,一个保存value),参数名和参数value会一一对应的。
通过上述方式就可以动态定义一个类的属性和方法了。
4. 自定义dynamic动态类
ExpandoObject类型的参数所有方法和属性都是动态的,但是如果我们需要创建一个类型,有些属性是可以在设计时定下来的,但有些类型不能在设计里定下来,对于这种情况我们可以继承ExpandoObject类:
class Myclass1:ExpandoObject { int Ensure_Property1; int Ensure_Method1; } dynamic dx=new Myclass1(); dx.Dynamic_Property1=1;
class ExpandoObject:IDynamicMetaObjectProvider{ public ExpandoObject(){}; }
如果想要在动态类中添加函数,除了先前的dalegate方法外,还可以使用DynamicObject类型提供的TryGetMember方法;DynamicObject类型也是实现了IDynamicMetaObjectProvider接口,不过里面还添加了易于使用的方法,用户可以override想要修改的方法,从而定制自己的类型工作方式:
public class DynamicObject : IDynamicMetaObjectProvider { [TargetedPatchingOptOut ( "Performance critical to inline this type of method across NGen image boundaries" )] protected DynamicObject ( ); public virtual IEnumerable<string> GetDynamicMemberNames ( ); public virtual DynamicMetaObject GetMetaObject ( Expression parameter ); public virtual bool TryBinaryOperation ( BinaryOperationBinder binder, object arg, out object result ); public virtual bool TryConvert ( ConvertBinder binder, out object result ); public virtual bool TryCreateInstance ( CreateInstanceBinder binder, object[ ] args, out object result ); public virtual bool TryDeleteIndex ( DeleteIndexBinder binder, object[ ] indexes ); public virtual bool TryDeleteMember ( DeleteMemberBinder binder ); public virtual bool TryGetIndex ( GetIndexBinder binder, object[ ] indexes, out object result ); public virtual bool TryGetMember ( GetMemberBinder binder, out object result ); public virtual bool TryInvoke ( InvokeBinder binder, object[ ] args, out object result ); public virtual bool TryInvokeMember ( InvokeMemberBinder binder, object[ ] args, out object result ); public virtual bool TrySetIndex ( SetIndexBinder binder, object[ ] indexes, object value ); public virtual bool TrySetMember ( SetMemberBinder binder, object value ); public virtual bool TryUnaryOperation ( UnaryOperationBinder binder, out object result ); }
不用全部方法都实现,只需实现你会用到的就好,比如用户修改TryGetMember方法从而定制自己的属性geter可以如下:
(摘自Programming C#4.0)
using System; using System.Dynamic; public class CustomDynamic:DynamicObject { private static DateTime FirstSighting=new DateTime(1947,3,13); public override bool TryGetMember(GetMemberBinder binder, out object result) { var compare=binder.IgnoreCase?StringComparer.InvariantCultureIgnoreCase:StringComparer.InvariantCulture; if(compare.Compare(binder.Name, “Brigadoon”)==0) { //Brigadoon famous for appearing only once eary 100 years. DateTime today=DateTime.Now.Date; if(today.DayOfYear==FirstSighting,DayOfYear) { //Right day, what about the year? int yearsSinceFirstSighting = today.Year-FirstSighting.Year; if(yearsSinceFirstSighting%100==0) { result=”Welcome to Brigadoon. Please drive carefully.”; return true; } } } return base.TryGetMember(binder, out result); } }
这样的话调用dx.Brigadoon时就会处理了。
GetMemberBinder中binder.Name能知道属性名字,binder.IgnoreCase能知道属性是否大小写敏感,默认是Case_Sensitive的,不过某些语言(比如VB)是Case_Insensitive。如此,实现DynamicObject类的对应方法能使类型更加灵活。
总结:
dynamic能较好的处理动态类型,配置动态类。特别是程序运行前不能确定类结构的情况,使用dynamic能很好的解决这个问题。
不过dynamic不能滥用,如果程序能确定类构造形式,则还是推荐使用静态的声明,首先静态声明不影响程序运行效率,而且静态声明能够在编译期检查出类型错误,从而避免运行时函数崩溃。