dynamic设计动态类 (C# 4.0)

 

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();
以上代码是没有问题,不过dynamic修饰的参数不能调用该类型的static函数。虽说static函数一般为ClassA::Static_Method1()这样的调用方式,不会出现d.Static_Method1()这种状况,但是在“扩展方法”中就会出现这种状况了,如果实现了如下扩展方法:
static class ExpandMethod
{    
          public static int Add_ClassA(this ClassA a){…}
}
这样子的调用d.Add_ClassA()在运行期会抛出RuntimeBinderException这样的异常,如果声明为非dynamic参数ClassA a=new ClassA()这样就可以正常的调用a.Add_ClassA()。

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);
以上代码能为expando添加公用属性和方法,这也是动态类的一种创建方式。需要注意的是添加共用方法时不能直接添加lambda表达式,dynamic需接收delegate类型。

ExpandoObject里面有一个类似Dictionary的结构(确切的说有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;
其实实现这种动态添加属性的根本是实现IDynamicMetaObjectProvider接口,该接口在DLR(动态语言运行时)里定义,ExpandoObject就是实现了这个接口的:
class ExpandoObject:IDynamicMetaObjectProvider{
          public ExpandoObject(){};
}
PS: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 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不能滥用,如果程序能确定类构造形式,则还是推荐使用静态的声明,首先静态声明不影响程序运行效率,而且静态声明能够在编译期检查出类型错误,从而避免运行时函数崩溃。

你可能感兴趣的:(学习笔录)