C#4.0新特性之(一)动态查找
在大神Anders的领导下,C#这门语言也越来越快地朝着编程语言宇宙第一神器进化,C#4.0的新特征都是围绕“动态”(dynamic)的概念的,本文我们先来看看第一个新特性:动态查找(Dynamic Lookup)。
1.初识dynamic
动态查找允许动态(即在运行时)实现对某个对象的操作与对象类型的绑定,而不管这个对象是来自COM,IronPython,HTML DOM还是CLR的反射。你可以在程序中绕过编译器的类型检查,而把类型的匹配(lookup)丢给运行时去作。如果你需要对这样的对象进行操作,则会用到一个全新的类型:dynamic
dynamic是一个和之前所有CTS支持的类型都很不一样的类型,因为他不是object!确切的说,它会告知编译器“请暂时别把我当成任何object!”。看上去这和过去的反射很类似,但是dynamic可以让我们在代码里就可以直接实现对这个未知类型对象的操作,下面我们通过一个例子来说明dynamic带来的便利。我的电脑上安装了一种叫X雷的下载软件,它提供了一些COM组件可供调用,在过去,我需要这样来调用这个COM对象:
without Dynamic
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
private
static
void
NoDynamicCall(Stringurl
=
"
http://www.sunhao.cc/temp/lgxz.wma
"
)
{
TypethunderAgent;
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;
//
refurl
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
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
TypeagentType;
if
(url
!=
null
&&
url.Length
>
0
)
{
agentType
=
Type.GetTypeFromProgID(
"
ThunderAgent.Agent
"
);
dynamicdAgent
=
Activator.CreateInstance(agentType);
dAgent.AddTask5(url,
""
,
""
,
""
,url,
-
1
,
0
,
-
1
,
""
,
""
,
""
,
1
,
""
,
-
1
);
dAgent.CommitTasks2(
1
);
}
这样直接的调用方式要自然多了。不过你也许会问,既然这里的dAgent的类型未知,而其AddTask5方法在编译时也完全不知道其存在性,那么岂不是任何合法或者非法的调用都不会受到编译器的监管,而把一切可能的危险留给了运行时?的确,编译器只会检查发生在CTS支持的各种类型上的调用,而dynamic在编译时还没有被映射到任何一种CTS类型。
Tips 前面说的dynamic不是object句话当且仅当程序运行前是正确的,运行时dynamic会首先被声明成为一个object,下面是IL描述的分配本地参数的stack上的信息:
dynamic IL
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
.method
private
hidebysig
static
void
DynamicCall([opt]
string
url)
cil
managed
{
.param[
1
]=
"
http://www.sunhao.cc/temp/lgxz.wma
"
//
Codesize420(0x1a4)
.maxstack
17
.locals
init
([
0
]
class
[mscorlib]System.TypeagentType,
[
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有风险,使用需谨慎。
2.DLR与自定义动态类型
Dynamic Language Runtime是.Net 4.0中一组全新的API。对于C#,DLR提供了Microsoft.CSharp.RuntimeBinder命名空间[1],它为C#提供了强大的运行时互操作(COM,Ironpython等)能力,DLR也有优秀的缓存机制,对象一旦被成功绑定,CLR在下一次调用的时候就可以直接对确定类型的对象进行操作,而不必再通过DLR去lookup了。如果想在自己的代码中实现一个动态类型对象,可以继承DynamicObject[2]类,并实现自己的若干get和set方法。例如下面这个简单的例子:
MyClass
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
public
class
MyClass:DynamicObject
{
public
override
bool
TryInvokeMember(InvokeMemberBinderbinder,
object
[]args,
out
object
result)
{
result
=
binder.Name;
return
true
;
}
}
上述代码在尝试invoke某个方法的时候直接返回该方法的名字。于是下面的代码将输出方法名:
代码
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
dynamicd
=
new
MyClass();
Console.WriteLine(d.AnyMember());
3.dynamic的使用
由于dynamic本身也是一个类型(虽然只有编译器认识它,运行时不认识它),而且dynamic实现了implicit和explicit运算符,理论上任何可以使用CLR的类型的地方都可以用dynamic。以下的代码是合法的:
dynamic use case
<!--<br/ /><br/ />Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ />http://www.CodeHighlighter.com/<br/ /><br/ />-->
dynamicd
=
(dynamic)
2
;
Action
<
dynamic
>
dAct
=
new
Action
<
dynamic
>
((dynamicn)
=>
{Console.Write(n.GetType()
+
"
:
"
+
n);});
dAct(d);
但是dynamic也不是万能的:
1).目前动态查找不支持扩展方法的调用(可能在未来的版本的C#中会提供支持)。
2).匿名方法和Lambda表达式不能转换为dynamic,也就是说dynamic d = x=>x;是不合法的,事实上lambda表达式也不能转成object。一样的道理,因为lambda表达式会在上下文环境下要么被编译器解释成委托类型,要么被解释成表达式树,但是如果上下文缺乏类型信息,编译器会confuse掉。
4.总结
dynamic是C#4.0的核心特征,感觉上是C#这种强类型的语言多了一些动态语言的特征,是对C#和.Net的一个完善。如本文开头所说,作为一门编程语言,C#正在猛练北冥神功[3] ,这样下去可能C#要和ms word一样成为居家旅行杀人越货必备的武器了。
5.引用
[1] http://msdn.microsoft.com/en-us/library/microsoft.csharp.runtimebinder(VS.100).aspx
[2] http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject(VS.100).aspx
[3] http://baike.baidu.com/view/146278.htm
Author:Freesc Huang @ CNBlogs