您的位置:首页 > 编程语言 > C#

C#学习笔记(十一):动态类型

2015-07-13 19:26 477 查看
C#是一门静态类型的语言,但是在C#4.0时微软引入了动态类型的概念。

dynamic

关键字dynamic用来定义动态对象,我们来看一下动态类型的一些特性。

调用不同类的相同方法

我们有两个或多个不相关的类,然后运行时需要可以调用到相同名称的方法,如下:

using System;

namespace Study
{
class Program
{
static void Main(string[] args)
{
dynamic obj = GetObject(0);
Console.WriteLine(obj.Talk());

Console.Read();
}

private static Object GetObject(int type)
{
switch (type)
{
case 1:
return new Dog();
}
return new Robot();
}
}

public class Dog
{
public string Talk()
{
return "Wang Wang!";
}
}

public class Robot
{
public string Talk()
{
return "I`m a Robot!";
}
}
}


我们的两个类没有继承也没有应用相同的接口,但是可以调用到相同的方法,使用GetObject(1)可以得到想要的结果。

这就是动态类型,在编译时不会对方法等进行判断,而是在运行时才进行处理,如果调用到不存在的方法才会报错。


C#编译器允许你通过dynamic对象调用任何方法,即使这个方法根本不存在,编译器也不会在编译的时候报编译错误。只有在运行的时候,它才会检查这个对象的实际类型,并检查在它上面Talk()是什么意思。动态类型将使得C#可以以更加统一而便利的形式表示下列对象:

来自动态编程语言——如Python或Ruby——的对象;

通过IDispatch访问的COM对象;

通过反射访问的一般.NET类型;

结构发生过变化的对象——如HTML DOM对象;

当我们得到一个动态类型的对象时,不管它是来自COM还是IronPython、HTML DOM还是反射,只需要对其进行操作即可,动态语言运行时(DLR)会帮我们指出针对特定的对象以及这些操作的具体意义。这将给我们的开发带来极大的灵活性,并且能够极大程度上地精简我们的代码。


动态类型使用注意

不能调用扩展方法;

委托与动态类型不能进行隐式转换;

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

类不能继承dynamic、泛型参数不能使用dynamic和接口实现也不能使用dynamic;

实现动态行为

实现动态行为有3种方法,分别可以用在不同的场合。

使用ExpandoObject类

直接使用ExpandoObject类来实现动态行为,代码如下:

using System;
using System.Dynamic;

namespace Study
{
class Program
{
static void Main(string[] args)
{
dynamic obj = new ExpandoObject();
//添加属性
obj.name = "Li Lei";
obj.age = 20;
//添加方法
obj.Add = (Func<int, int, int>) ((a, b) => a + b);

Console.WriteLine("Name: " + obj.name);
Console.WriteLine("Age: " + obj.age);
Console.WriteLine("Add: " + obj.Add(100, 123));

Console.Read();
}
}
}


输出如下:

Name: Li Lei
Age: 20
Add: 223


继承DynamicObject类

通过继承DynamicObject类也可以实现动态效果,示例如下:

using System;
using System.Dynamic;

namespace Study
{
class Program
{
static void Main(string[] args)
{
dynamic obj = new MyClass();
obj.name = "Li Lei";
obj.age = 20;
obj.CallFunc();

Console.Read();
}
}

public class MyClass : DynamicObject
{
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Console.WriteLine("设置" + binder.Name + "为" + value);
return true;
}

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
Console.WriteLine("调用" + binder.Name + "方法");
result = null;
return true;
}
}
}


输出如下:

设置name为Li Lei
设置age为20
调用CallFunc方法


实现IDynamicMetaObjectProvider接口

如果已经继承了其它的类,则可以通过实现IDynamicMetaObjectProvider接口来实现动态行为,例子如下:

using System;
using System.Dynamic;
using System.Linq.Expressions;

namespace Study
{
class Program
{
static void Main(string[] args)
{
dynamic obj = new MyClass();
obj.CallFunc();

Console.Read();
}
}

public class MyClass : IDynamicMetaObjectProvider
{
public DynamicMetaObject GetMetaObject(Expression parameter)
{
Console.WriteLine("获取元数据");
return new MetaDynamic(parameter, this);
}
}

public class MetaDynamic : DynamicMetaObject
{
public MetaDynamic(Expression expression, object value) : base(expression, BindingRestrictions.Empty, value)
{
}

public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
MyClass target = base.Value as MyClass;
Expression self = Expression.Convert(base.Expression, typeof (MyClass));
var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
Console.WriteLine("调用" + binder.Name + "方法");
return new DynamicMetaObject(self, restrictions);
}
}
}


输出如下:

获取元数据
调用CallFunc方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: