频道栏目
读书频道 > 软件开发 > C# > Visual C#2010开发权威指南
2.1.1 C# 4.0新特性 - 动态查找
2012-12-08 09:40:43     我来说两句
收藏   我要投稿

本文所属图书 > Visual C#2010开发权威指南

Visual Studio是微软公司推出的开发环境,是目前最流行的Windows平台应用程序开发环境。Visual Studio 2010版本于2010年4月12日面市,其集成开发环境(IDE)的界面被重新设计和组织,变得更加简单明了。Visual ...  立即去当当网订购

动态查找允许动态(即在运行时)实现对某个对象的操作和对象类型的绑定,而不管这个对象是来自COM、IronPython、HTML DOM还是CLR的反射。可以在程序中绕过编译器的类型检查,而把类型的匹配(Lookup)丢给运行时去作。如果需要对这样的对象进行操作,则会用到一个全新的类型dynamic。

dynamic是一个与先前所有CTS支持的类型都很不一样的类型,因为不是object!

确切地说,它会告知编译器“请暂时别把我当成任何object!”

看上去这与过去的反射很类似,但是dynamic让我们在代码里就可以直接实现对这个未知类型对象的操作,下面通过一个例子来说明dynamic带来的便利。作者的电脑上安装了一种下载软件,它提供了一些COM组件可供调用,在过去,需要这样来调用这个COM对象:
// without Dynamic
private static void NoDynamicCall(
String url=http://www.sunhao.cc/temp/lgxz.wma)
{
Type thunderAgent;
object objThunderAgent;
object []parameter = new object[14];
if (url!=null && url.Length>0)
{
thunderAgent = Type.GetTypeFromProgID("ThunderAgent.Agent");
objThunderAgent = Activator.CreateInstance(thunderAgent);
parameter[0] = url;//url
parameter[1] = "";
parameter[2] = "";
parameter[3] = "";
parameter[4] = url; //ref url
parameter[5] = -1;
parameter[6] = 0;
parameter[7] = -1; //threadCount
parameter[8] = ""; //strCookie
parameter[9] = "";
parameter[10] = "";
parameter[11] = 1;
parameter[12] = "";
parameter[13] = -1;
thunderAgent.InvokeMember("AddTask5", BindingFlags.InvokeMethod,
null, objThunderAgent, parameter);
object []parm = new object[1] { 1 };
thunderAgent.InvokeMember("CommitTasks2",
BindingFlags.InvokeMethod, null, objThunderAgent, parm);
}
}

这种通过Type.InvokeMemer在COM对象上调用方法实属别扭且无奈之举,因为编译器要先对方法的调用者进行类型绑定。不过现在有了dynamic类型,我们可以按照这样的方式对上述COM对象进行操作了:
// with Dynamic
Type agentType;
if (url!=null && url.Length>0)
{
agentType = Type.GetTypeFromProgID("ThunderAgent.Agent");
dynamic dAgent = Activator.CreateInstance(agentType);
dAgent.AddTask5(url, "", "", "", url, -1, 0, -1, "", "", "", 1, "", -1);
dAgent.CommitTasks2(1);
}

这样直接的调用方式要自然多了。不过也许有人会问,既然这里的dAgent的类型未知,而其AddTask5方法在编译时也完全不知道其存在性,那么岂不是任何合法或者非法的调用都不会受到编译器的监管,而把一切可能的危险留给了运行时?的确,编译器只会检查发生在CTS支持的各种类型上的调用,而dynamic在编译时还没有被映射到任何一种CTS类型。

提示一下,前面说的dynamic不是object这句话当且仅当程序运行前是正确的,运行时dynamic会首先被声明成为一个object,下面是IL描述的分配本地参数的stack上的信息:
// dynamic IL
.method private hidebysig static void  DynamicCall([opt] string url) cil managed
{
.param [1] = "http://www.sunhao.cc/temp/lgxz.wma"
// Code size       420 (0x1a4)
.maxstack  17
.locals init ([0] class [mscorlib]System.Type agentType,
[1] object dAgent,
[2] bool CS$4$0000,
[3] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder
.CSharpArgumentInfo[] CS$0$0001)
//以下省略N行

而编译器对任何对发生在dynamic类型上的操作无能为力,它能做的唯一工作就是为运行时收集一些该dynamic对象的信息,比如它上面的方法签名。dynamic提供了访问COM对象的方便,但是由于它在一定程度上破坏了C#强类型的特性,同时也要求程序员对自己写下的代码完全负责,增加了Debug的成本。

所以说dynamic有风险,使用需谨慎。

DLR和自定义动态类型Dynamic Language Runtime是.NET 4.0中一组全新的API。对于C#,DLR提供了Microsoft.CSharp.RuntimeBinder命名空间,它为C#提供了强大的运行时互操作(COM、Ironpython等)能力,DLR也有优秀的缓存机制,对象一旦被成功绑定,CLR在下一次调用的时候就可以直接对确定类型的对象进行操作,而不必再通过DLR去Lookup了。如果想在自己的代码中实现一个动态类型对象,可以继承DynamicObject类,并实现自己的若干get和set方法。
例如下面这个简单的例子:
//MyClass
public class MyClass : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result)
{
result = binder.Name;
return true;
}
}
dynamic d = new MyClass();
Console.WriteLine(d.AnyMember());

由于dynamic本身也是一个类型(虽然只有编译器认识它,运行时不认识它),而且dynamic实现了implicit和explicit运算符,理论上任何可以使用CLR的类型的地方都可以用dynamic。以下的代码是合法的:
// dynamic use case
dynamic d = (dynamic)2;
Action<dynamic> dAct = new Action<dynamic>((dynamic n) => { Console.Write(n.GetType()+": "+n); });
dAct(d);

但是dynamic也不是万能的:

目前动态查找不支持扩展方法的调用(可能在未来的版本的C#中会提供支持)。

匿名方法和Lambda表达式不能转换为dynamic,也就是说dynamic d = x=>x;是不合法的,事实上Lambda表达式也不能转成object。同样的道理,Lambda表达式会在上下文环境下要么被编译器解释成委托类型,要么被解释成表达式树。但是如果上下文缺乏类型信息,编译器会无所适从了。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:概述
下一篇:2.1.2 C# 4.0新特性 - dynamic类型
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站