• 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修饰,比如:

     1 class ClassA{
    2 public int Property1{get;set;}
    3 public int Property2{get;set;}
    4 public int Method1(){…}
    5 public static int Static_Method1(){…};
    6 }
    7
    8 dynamic d=new ClassA();
    9 d.Property1=23;
    10 d.Property2=@”Hello, Word!”;
    11 d.Method1();

    以上代码是没有问题,不过dynamic修饰的参数不能调用该类型的static函数。虽说static函数一般为ClassA::Static_Method1()这样的调用方式,不会出现d.Static_Method1()这种状况,但是在“扩展方法”中就会出现这种状况了,如果实现了如下扩展方法:

    1 static class ExpandMethod{
    2 public static int Add_ClassA(this ClassA a){…}
    3 }

    这样子的调用d.Add_ClassA()在运行期会抛出RuntimeBinderException这样的异常,如果声明为非dynamic参数ClassA a=new ClassA()这样就可以正常的调用a.Add_ClassA()。

    3. dynamic和类属性方法

    没有的属性和方法是不能调用的。d.Property3=30, d.Method2()这样的代码虽说在编译器不会报警,但是在运行期会有RuntimeBinderException异常。

    若不能确定类方法属性,则需要ExpandoObject类,下面是示例:

    1 dynamic expando=new ExpandoObject();
    2 expando.Property1=true;
    3 expando.Property2=23;
    4 expando.Property3=@”Hello Expando!”;
    5 delegate int int_add(int a, int b);
    6 expando.Method1=new int_add((a,b)=>(a+b));
    7 expando.Property4=expando.Method1(12,34);

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

    ExpandoObject里面有一个类似Dictionary<string, object>的结构(确切的说有2个列表,一个保存key,一个保存value),参数名和参数value会一一对应的。

    通过上述方式就可以动态定义一个类的属性和方法了。

    4. 自定义dynamic动态类

    ExpandoObject类型的参数所有方法和属性都是动态的,但是如果我们需要创建一个类型,有些属性是可以在设计时定下来的,但有些类型不能在设计里定下来,对于这种情况我们可以继承ExpandoObject类:

    1 class Myclass1:ExpandoObject{
    2 int Ensure_Property1;
    3 int Ensure_Method1;
    4 }
    5
    6 dynamic dx=new Myclass1();
    7 dx.Dynamic_Property1=1;

    其实实现这种动态添加属性的根本是实现IDynamicMetaObjectProvider接口,该接口在DLR(动态语言运行时)里定义,ExpandoObject就是实现了这个接口的:

    1 class ExpandoObject:IDynamicMetaObjectProvider{}
    2 public ExpandoObject(){};
    3 }

    PS:ExpandoObject还会实现其它接口,没有在此写出来。

    如果想要在动态类中添加函数,除了先前的dalegate方法外,还可以使用DynamicObject类型提供的TryGetMember方法;DynamicObject类型也是实现了IDynamicMetaObjectProvider接口,不过里面还添加了易于使用的方法,用户可以override想要修改的方法,从而定制自己的类型工作方式:

     1  public class DynamicObject : IDynamicMetaObjectProvider
    2 {
    3 [TargetedPatchingOptOut ( "Performance critical to inline this type of method across NGen image boundaries" )]
    4 protected DynamicObject ( );
    5 public virtual IEnumerable<string> GetDynamicMemberNames ( );
    6 public virtual DynamicMetaObject GetMetaObject ( Expression parameter );
    7 public virtual bool TryBinaryOperation ( BinaryOperationBinder binder, object arg, out object result );
    8 public virtual bool TryConvert ( ConvertBinder binder, out object result );
    9 public virtual bool TryCreateInstance ( CreateInstanceBinder binder, object[ ] args, out object result );
    10 public virtual bool TryDeleteIndex ( DeleteIndexBinder binder, object[ ] indexes );
    11 public virtual bool TryDeleteMember ( DeleteMemberBinder binder );
    12 public virtual bool TryGetIndex ( GetIndexBinder binder, object[ ] indexes, out object result );
    13 public virtual bool TryGetMember ( GetMemberBinder binder, out object result );
    14 public virtual bool TryInvoke ( InvokeBinder binder, object[ ] args, out object result );
    15 public virtual bool TryInvokeMember ( InvokeMemberBinder binder, object[ ] args, out object result );
    16 public virtual bool TrySetIndex ( SetIndexBinder binder, object[ ] indexes, object value );
    17 public virtual bool TrySetMember ( SetMemberBinder binder, object value );
    18 public virtual bool TryUnaryOperation ( UnaryOperationBinder binder, out object result );
    19 }

      

    不用全部方法都实现,只需实现你会用到的就好,比如用户修改TryGetMember方法从而定制自己的属性geter可以如下:

    (摘自Programming C#4.0)

     1 using System;
    2 using System.Dynamic;
    3 public class CustomDynamic:DynamicObject{
    4 private static DateTime FirstSighting=new DateTime(1947,3,13);
    5 public override bool TryGetMember(GetMemberBinder binder, out object result){
    6 var compare=binder.IgnoreCase?StringComparer.InvariantCultureIgnoreCase:StringComparer.InvariantCulture;
    7 if(compare.Compare(binder.Name, “Brigadoon”)==0){
    8 //Brigadoon famous for appearing only once eary 100 years.
    9 DateTime today=DateTime.Now.Date;  
                                  if(today.DayOfYear==FirstSighting,DayOfYear){
    10 //Right day, what about the year?
    11 int yearsSinceFirstSighting = today.Year-FirstSighting.Year;
    12 if(yearsSinceFirstSighting%100==0){
    13 result=”Welcome to Brigadoon. Please drive carefully.”;
    14 return true;
    15 }
    16 }
    17 }
    18 return base.TryGetMember(binder, out result);
    19 }
    20 }

      

    这样的话调用dx.Brigadoon时就会处理了。

    GetMemberBinder中binder.Name能知道属性名字,binder.IgnoreCase能知道属性是否大小写敏感,默认是Case_Sensitive的,不过某些语言(比如VB)是Case_Insensitive。如此,实现DynamicObject类的对应方法能使类型更加灵活。

    总结

    dynamic能较好的处理动态类型,配置动态类。特别是程序运行前不能确定类结构的情况,使用dynamic能很好的解决这个问题。

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

  • 相关阅读:
    nginx配置选项try_files详解
    Centos7.4 升级openSSH的操作步骤
    docker运行postgresql出现could not locate a valid checkpoint record的产生原因及如何解决
    浅析JavaScript类型化数组TypedArray理解、为什么使用TypedArray、类型数组与普通数组的区别及其常见应用(处理二进制数据类型)
    浅析Uint8Array语法及常见使用、Uint8Array.slice与Uint8Array.subarray区别(是否指向同一个内存空间)、new Uint8Array(typedArray)构造函数对typedArray的引用问题(保持同一个引用)、Uint8Array与String互相转换
    浅析JavaScript如何检测文件的类型:区分文件类型的本质、关于魔数的理解、input accept文件检测存在的问题、如何检测修改后缀名后的文件类型、推荐一个文件检测JS库-file-type库及其检测原理
    推荐4款高星星JS库:canvas库-Fabric.js、JavaScript客户端文件上传库-FilePond、客户端保存文件解决方案-FileSaver、JavaScript在线解压 ZIP 文件-JSZip
    浅析 ProgressEvent 接口及其应用:上传文件进度事件
    浅析Array.reduce()语法、reduce执行流程、常见使用(所有值求和、提供初始值累加所有值、二维转一维、计算元素出现个数、按属性分类、顺序执行promise、功能型管道函数等)
    725. 完全数
  • 原文地址:https://www.cnblogs.com/ganmuren/p/2108755.html
Copyright © 2020-2023  润新知