zoukankan      html  css  js  c++  java
  • 静中有动-动态类型

    在C#4.0中,最核心的特性莫过于动态类型的引入。

    1、动态类型简介

        一直在强调C#是一门静态类型的语言,因为它在定义变量时要明确给出变量的类型。例如在int i=5;这样的代码中,int就是变量i的类型,如果定义变量时没有明确指定变量的类型,则这样的代码是通过不了编译的。

        在C#4.0中,微软引入了dynamic关键字来定义动态类型。当我们使用由dynamic关键字限制的变量时,编译器并不知道它的类型,该类型只能在程序运行的时候才能被确定。动态类型的定义如下面的代码所示:

        dynamic i=5;

        从上面这行代码可以看出,定义动态类型的过程非常简单,只需要把之前的int类型修改为dynamic关键字即可。

        那静态类型和动态类型到底有什么不同呢?以代码来说明:

        object obj=10;

        obj=obj+10;//编译错误

        dynamic i=10;//动态类型定义

         i=i+10;

        在以上的代码中,第一行obj为object类型,而编译器却检测出“+”运算符无法应用于object和int类型。要让代码通过编译,必须使用强制类型转换。动态类型定义就不需要转换了,动态类型定义的变量的具体类型只能在程序运行时被确定,编译器根本不知道其类型是什么,所以也就不会出现编译错误了。

    2、C#为什么要引入动态类型

         2.1 可以减少强制类型转换的使用

         2.2 调用Python等动态语言

               动态类型使得C#语言调用Python这样的动态语言成为了可能。比如:

    1 ScriptEngine engine=Python.CreateEngine();
    2 Console.WriteLine("调用Python语言的print函数输出:");
    3 engine.Execute("print 'Hello world'");
    4 Console.Read();

        以上代码实现了在C#中调用Python的print函数完成消息打印的过程,但要使这段代码能够运行,还必须下载IronPython。IronPython是在.NET Framework上实现的一种动态语言。

        由于动态类型变量的具体类型只能在运行时确定,所以智能提示对于动态类型变量并不管用。开发人员在使用动态类型时,必须准确地知道类型所具有的成员,否则在程序运行时很难发现相关错误。

    3、动态类型约束

        动态类型的使用需要满足一下几个约束条件:

        3.1 不能用来调用扩展方法

              不能用动态类型作为参数来调用扩展方法,例如下面的代码将导致编译错误:

               var numbers=Enumerable.Range(10,10);

               dynamic number=4;

               var error=numbers.Take(number);//编译错误

               出现这种错误的原因在于:扩展方法是在另一个文件中被定义的,若参数为动态类型,编译器将无法确定参数的具体类型,因为也就不知道该导入哪个源文件了。我们可以通过两个方法来解决这个问题:

               第一种是将动态类型强制转换为正确的类型:var right1=numbers.Take((int)number);

               第二种是使用静态方法来调用扩展方法:var right2=Enumerable.Take(numbers,number);

               这两种方法虽然都可能,但还是不推荐在调用扩展方法时使用动态类型。

        3.2 委托与动态类型间不能做隐式转换

              不能将Lambda表达式定义为动态类型,因为他们之间不存在隐式转换,如下面的代码就会出现编译错误:

              dynamic lambdarestrict=x=>x+1;//编译时错误

              如果硬要把Lambda表达式定义为动态类型,就需要明确指定委托的类型,具体的解决方案如下:

              dynamic rightLambda=(Func<int,int>)(x=>x+1);

        3.3 不能调用构造函数和静态方法

              dynamic s=new dynamic();//编译错误   因为此时编译器无法指定具体的类型。

        3.4 类型声明和泛型类型参数

              不能将dynamic关键字用来基类声明,也不能将dynamic用于类型参数的约束,或作为类型所实现的接口的一部分。具体的实例代码如下:

     1 class DynamicBaseType:dynamic //基类不能为dynamic类型
     2 {
     3 }
     4 
     5 class DynamicTypeConstrain<T> where T:dynamic //dynamic类型不能用作类型参数
     6 {
     7 }
     8 
     9 class DynamicInterface :IEnumerable<dynamic>//dynamic不能作为所实现接口的一部分
    10 {
    11 }

    4、实现自己的动态行为

        引入静态类型后,我们便可为类型动态地添加属性和方法,从而实现我们自己的动态行为。具体来说,.NET中支持3种实现动态行为的方式:

        (1)使用ExpandoObject;

        (2)使用DynamicObject;

        (3)实现IDynamicMetaObjectProvider接口

        4.1 使用ExpandoObject来实现动态行为

             ExpandoObject是实现动态行为的最简单的方式,其实现代码如下:

             

     1 using System;
     2 using System.Dynamic;
     3 
     4 namespace 动态类型
     5 {
     6     class Program
     7     {
     8         static void Main(string[] args)
     9         {
    10             dynamic expand = new ExpandoObject();
    11             //动态绑定成员
    12             expand.Name = "Helius";
    13             expand.Age = 24;
    14             expand.Addmethod = (Func<int, int>) (x => x + 1);
    15             //调用类型成员
    16             Console.WriteLine($"enpand类型的姓名为:{expand.Name},年龄为:{expand.Age}");
    17             Console.WriteLine($"调用expand类型的动态帮东方法:{expand.Addmethod(5)}");
    18             Console.ReadKey();
    19         }
    20     }
    21 }

        以上代码通过使用ExpandoObject类,动态地为expand动态类型添加了属性和函数,并且expand类型也可以成功地被调用。

        4.2 使用DynamicObject来实现动态行为

             除了使用ExpandoObject,我们还可以通过重写DynamicObject来实现动态行为,具体的实现代码如下:

              

     1 using System;
     2 using System.Dynamic;
     3 
     4 namespace 动态类型
     5 {
     6     class Program
     7     {
     8         static void Main(string[] args)
     9         {
    10             dynamic dynamicobj=new DynamicType();
    11             dynamicobj.CallMethod();
    12             dynamicobj.Name = "Helius";
    13             dynamicobj.Age = 24;
    14             Console.ReadKey();
    15         }
    16     }
    17 
    18 
    19     class DynamicType:DynamicObject
    20     {
    21         //重写TryXXX方法,该方法表示对对象进行动态调用
    22         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    23         {
    24             Console.WriteLine($"{binder.Name}方法正在被调用");
    25             result = null;
    26             return true;
    27         }
    28 
    29         public override bool TrySetMember(SetMemberBinder binder, object value)
    30         {
    31             Console.WriteLine($"{binder.Name} 属性被设置,设置的值为{value}");
    32             return true;
    33         }
    34     }
    35 }

        

        4.3 使用IDynamicMetaObjectProvider接口来实现动态行为

              由于Dynamic类型是在运行过程中动态创建对象的,所以在对该类型的每个成员进行访问时,都会首先调用GetMethodObject方法来获得动态对象,然后再通过这个动态对象来完成调用。因为为了实现IDynamicMetaObjectProvider接口,我们需要先实现一个GetMetaObject方法,用以返回DynamicMetaObject对象。

          

     1 using System;
     2 using System.Dynamic;
     3 using System.Linq.Expressions;
     4 
     5 namespace 动态类型
     6 {
     7     class Program
     8     {
     9         static void Main(string[] args)
    10         {
    11 
    12             dynamic dynamicObj2=new DynamicType2();
    13             dynamicObj2.Call();
    14             Console.ReadKey();
    15         }
    16     }
    17 
    18 
    19     class DynamicType:DynamicObject
    20     {
    21         //重写TryXXX方法,该方法表示对对象进行动态调用
    22         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    23         {
    24             Console.WriteLine($"{binder.Name}方法正在被调用");
    25             result = null;
    26             return true;
    27         }
    28 
    29         public override bool TrySetMember(SetMemberBinder binder, object value)
    30         {
    31             Console.WriteLine($"{binder.Name} 属性被设置,设置的值为{value}");
    32             return true;
    33         }
    34     }
    35 
    36 
    37     internal class DynamicType2 : IDynamicMetaObjectProvider
    38     {
    39         public DynamicMetaObject GetMetaObject(Expression parameter)
    40         {
    41             Console.WriteLine("开始获得元数据......");
    42             return new Metadynamic(parameter, this);
    43         }
    44     }
    45 
    46     internal class Metadynamic : DynamicMetaObject
    47     {
    48         internal Metadynamic(Expression expression, DynamicType2 value):base(expression,BindingRestrictions.Empty,value)
    49         {
    50         }
    51 
    52         public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
    53         {
    54             //获得真正的对象
    55             DynamicType2 target = (DynamicType2) base.Value;
    56             Expression self = Expression.Convert(base.Expression, typeof (DynamicType2));
    57             var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
    58             //输出绑定方法名
    59             Console.WriteLine($"{binder.Name} 方法被调用了");
    60             return new DynamicMetaObject(self,restrictions);
    61         }
    62     }
    63 }

        结果为:

        

              

  • 相关阅读:
    (论坛答疑点滴)DataGrid模板列中控件的事件中怎么知道是哪行触发的事件?
    (论坛答疑点滴)怎么后台添加CheckBoxList并且得到选择结果
    (论坛答疑点滴)联合主键的情况怎么在DataGrid中利用DataKeys定位记录?
    (原创)按照一定的格式生成一定数量的随机数的例子
    (原创)DataGrid动态添加模板列的一个例子
    有的时候看似是对的往往是不对的
    (论坛答疑点滴)如何向某网址Post信息,并得到CookieContainer以便以后直接通过验证
    (论坛答疑点滴)怎么触发DataGrid模板列中控件的事件?
    (原创)利用vs.net快速开发windows服务(c#)
    (论坛答疑点滴)怎么给Table动态添加控件并且得到控件的值?
  • 原文地址:https://www.cnblogs.com/Helius/p/5792997.html
Copyright © 2011-2022 走看看