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不能滥用,如果程序能确定类构造形式,则还是推荐使用静态的声明,首先静态声明不影响程序运行效率,而且静态声明能够在编译期检查出类型错误,从而避免运行时函数崩溃。