C#是一门静态类型的语言,但是在C#4.0时微软引入了动态类型的概念。
dynamic
关键字dynamic用来定义动态对象,我们来看一下动态类型的一些特性。
调用不同类的相同方法
我们有两个或多个不相关的类,然后运行时需要可以调用到相同名称的方法,如下:
1 using System; 2 3 namespace Study 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 dynamic obj = GetObject(0); 10 Console.WriteLine(obj.Talk()); 11 12 Console.Read(); 13 } 14 15 private static Object GetObject(int type) 16 { 17 switch (type) 18 { 19 case 1: 20 return new Dog(); 21 } 22 return new Robot(); 23 } 24 } 25 26 public class Dog 27 { 28 public string Talk() 29 { 30 return "Wang Wang!"; 31 } 32 } 33 34 public class Robot 35 { 36 public string Talk() 37 { 38 return "I`m a Robot!"; 39 } 40 } 41 }
我们的两个类没有继承也没有应用相同的接口,但是可以调用到相同的方法,使用GetObject(1)可以得到想要的结果。
这就是动态类型,在编译时不会对方法等进行判断,而是在运行时才进行处理,如果调用到不存在的方法才会报错。
C#编译器允许你通过dynamic对象调用任何方法,即使这个方法根本不存在,编译器也不会在编译的时候报编译错误。只有在运行的时候,它才会检查这个对象的实际类型,并检查在它上面Talk()是什么意思。动态类型将使得C#可以以更加统一而便利的形式表示下列对象:
- 来自动态编程语言——如Python或Ruby——的对象;
- 通过IDispatch访问的COM对象;
- 通过反射访问的一般.NET类型;
- 结构发生过变化的对象——如HTML DOM对象;
当我们得到一个动态类型的对象时,不管它是来自COM还是IronPython、HTML DOM还是反射,只需要对其进行操作即可,动态语言运行时(DLR)会帮我们指出针对特定的对象以及这些操作的具体意义。这将给我们的开发带来极大的灵活性,并且能够极大程度上地精简我们的代码。
动态类型使用注意
- 不能调用扩展方法;
- 委托与动态类型不能进行隐式转换;
- 不能调用构造函数和静态方法;
- 类不能继承dynamic、泛型参数不能使用dynamic和接口实现也不能使用dynamic;
实现动态行为
实现动态行为有3种方法,分别可以用在不同的场合。
使用ExpandoObject类
直接使用ExpandoObject类来实现动态行为,代码如下:
1 using System; 2 using System.Dynamic; 3 4 namespace Study 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 dynamic obj = new ExpandoObject(); 11 //添加属性 12 obj.name = "Li Lei"; 13 obj.age = 20; 14 //添加方法 15 obj.Add = (Func<int, int, int>) ((a, b) => a + b); 16 17 Console.WriteLine("Name: " + obj.name); 18 Console.WriteLine("Age: " + obj.age); 19 Console.WriteLine("Add: " + obj.Add(100, 123)); 20 21 Console.Read(); 22 } 23 } 24 }
输出如下:
1 Name: Li Lei 2 Age: 20 3 Add: 223
继承DynamicObject类
通过继承DynamicObject类也可以实现动态效果,示例如下:
1 using System; 2 using System.Dynamic; 3 4 namespace Study 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 dynamic obj = new MyClass(); 11 obj.name = "Li Lei"; 12 obj.age = 20; 13 obj.CallFunc(); 14 15 Console.Read(); 16 } 17 } 18 19 public class MyClass : DynamicObject 20 { 21 public override bool TrySetMember(SetMemberBinder binder, object value) 22 { 23 Console.WriteLine("设置" + binder.Name + "为" + value); 24 return true; 25 } 26 27 public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 28 { 29 Console.WriteLine("调用" + binder.Name + "方法"); 30 result = null; 31 return true; 32 } 33 } 34 }
输出如下:
1 设置name为Li Lei 2 设置age为20 3 调用CallFunc方法
实现IDynamicMetaObjectProvider接口
如果已经继承了其它的类,则可以通过实现IDynamicMetaObjectProvider接口来实现动态行为,例子如下:
1 using System; 2 using System.Dynamic; 3 using System.Linq.Expressions; 4 5 namespace Study 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 dynamic obj = new MyClass(); 12 obj.CallFunc(); 13 14 Console.Read(); 15 } 16 } 17 18 public class MyClass : IDynamicMetaObjectProvider 19 { 20 public DynamicMetaObject GetMetaObject(Expression parameter) 21 { 22 Console.WriteLine("获取元数据"); 23 return new MetaDynamic(parameter, this); 24 } 25 } 26 27 public class MetaDynamic : DynamicMetaObject 28 { 29 public MetaDynamic(Expression expression, object value) : base(expression, BindingRestrictions.Empty, value) 30 { 31 } 32 33 public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) 34 { 35 MyClass target = base.Value as MyClass; 36 Expression self = Expression.Convert(base.Expression, typeof (MyClass)); 37 var restrictions = BindingRestrictions.GetInstanceRestriction(self, target); 38 Console.WriteLine("调用" + binder.Name + "方法"); 39 return new DynamicMetaObject(self, restrictions); 40 } 41 } 42 }
输出如下:
1 获取元数据 2 调用CallFunc方法
时间: 2024-10-25 10:15:10