C#编程:静中有动-动态类型
Vivian 2018-05-22 来源 : 阅读 1225 评论 0

摘要:本次的C#编程的学习中,我们知道在C#4.0中,最核心的特性莫过于动态类型的引入。是静中有动-动态类型,希望对大家学习C#编程有所帮助。

本次的C#编程的学习中,我们知道在C#4.0中,最核心的特性莫过于动态类型的引入。是静中有动-动态类型,希望对大家学习C#编程有所帮助。

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 }</dynamic></t>

   

 

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 }</int,>

   

以上代码通过使用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 }

   

结果为:

 C#编程:静中有动-动态类型

本文由职坐标整理并发布,了解更多内容,请关注职坐标编程语言C#.NET频道!

本文由 @Vivian 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程