c#4.0新特性之一: Dynamic Lookup (1)

废话不多说,直接开始。先看程序:

Code
1class Program
2{
3 static void Main(string[] args)
4 {
5 dynamic foo1 = new Foo();
6        foo1.Do1();
7        foo1.Do2();
8 foo1.KissFanweixiao();
9    }
10}
11
12public class Foo
13{
14 public void Do1() { Console.WriteLine("fanweixiao calls do1"); }
15 public void Do2() { Console.WriteLine("fanweixiao calls do2"); }
16}

这段代码在vs2010中可以编译通过。但是在运行的时候会报错。这是必然,KissFanweixiao()这个方法无中生有嘛。

这里注意到这是个RuntimeBinderException,而内容是不能找到KissFanweixiao的符号。

dynamic这个关键字看起来跟3.0增加的var很像,但是从IL的角度上来看,就是天壤之别了,毕竟var是编译时就能确定的东西,而dynamic追求的就是在运行时再决定究竟是个什么东西。我们还是先注释掉foo1.KissFanweixiao();这一句,使程序可以正常运行,然后看ILDASM的结果:

多出一个<Main>o__SiteContainer0类,这个类下的<>p__Site1...和<>p__Site2...对应着Do1()和Do2()两个方法的Invoke。再细看此时的Main函数:

Code
.method private hidebysig static void  Main(string[] args) cil managed
{
.entrypoint
// Code size       139 (0x8b)
.maxstack 7
.locals init ([0] object foo1)
IL_0000: newobj instance void LearnCSharp4.Foo::.ctor()
IL_0005: stloc.0
IL_0006: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
IL_000b: brtrue.s   IL_0033
IL_000d: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
IL_0012: ldc.i4.0
IL_0013: ldc.i4.0
IL_0014: ldstr "Do1"
IL_0019: ldtoken    [mscorlib]System.Object
IL_001e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0023: ldnull
IL_0024: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
bool,
bool,
string,
class [mscorlib]System.Type,
class [mscorlib]System.Type[])
IL_0029: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
IL_002e: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
IL_0033: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
IL_0038: ldfld      !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Target
IL_003d: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
IL_0042: ldloc.0
IL_0043: callvirt instance void class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>::Invoke(!0,
                                                                                                                                             !1)
IL_0048: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
IL_004d: brtrue.s   IL_0075
IL_004f: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
IL_0054: ldc.i4.0
IL_0055: ldc.i4.0
IL_0056: ldstr "Do2"
IL_005b: ldtoken    [mscorlib]System.Object
IL_0060: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0065: ldnull
IL_0066: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
bool,
bool,
string,
class [mscorlib]System.Type,
class [mscorlib]System.Type[])
IL_006b: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
IL_0070: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
IL_0075: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
IL_007a: ldfld      !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Target
IL_007f: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
IL_0084: ldloc.0
IL_0085: callvirt instance void class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>::Invoke(!0,
                                                                                                                                             !1)
IL_008a: ret
} // end of method Program::Main

如果是var或Foo的话直接这样就完事了:

Code
1 IL_0000: newobj instance void LearnCSharp4.Foo::.ctor()
2 IL_0005: stloc.0
3 IL_0006: ldloc.0
4 IL_0007: callvirt instance void LearnCSharp4.Foo::Do1()
5 IL_000c: ldloc.0
6 IL_000d: callvirt instance void LearnCSharp4.Foo::Do2()
7 IL_0012: ret

OK,看出不同来了。然后取消掉对foo.KissFanweixiao();的注释,再编译,再用ILDASM看,Do1()和KissFanWeixiao()的地位应该是一样的——因为dynamic与编译时无关嘛(好像是废话...但是这点很重要)。我们再看一眼有三个方法后的MSIL:

Code
1.method private hidebysig static void  Main(string[] args) cil managed
2{
3 .entrypoint
4 // Code size       205 (0xcd)
5 .maxstack 7
6 .locals init ([0] object foo1)
7 IL_0000: newobj instance void LearnCSharp4.Foo::.ctor()
8 IL_0005: stloc.0
9 IL_0006: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
10 IL_000b: brtrue.s   IL_0033
11 IL_000d: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
12 IL_0012: ldc.i4.0
13 IL_0013: ldc.i4.0
14 IL_0014: ldstr "Do1"
15 IL_0019: ldtoken    [mscorlib]System.Object
16 IL_001e: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
17 IL_0023: ldnull
18 IL_0024: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
19 bool,
20 bool,
21 string,
22 class [mscorlib]System.Type,
23 class [mscorlib]System.Type[])
24 IL_0029: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
25 IL_002e: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
26 IL_0033: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
27 IL_0038: ldfld      !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Target
28 IL_003d: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site1'
29 IL_0042: ldloc.0
30 IL_0043: callvirt instance void class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>::Invoke(!0,
31                                                                                                                                             !1)
32 IL_0048: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
33 IL_004d: brtrue.s   IL_0075
34 IL_004f: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
35 IL_0054: ldc.i4.0
36 IL_0055: ldc.i4.0
37 IL_0056: ldstr "Do2"
38 IL_005b: ldtoken    [mscorlib]System.Object
39 IL_0060: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
40 IL_0065: ldnull
41 IL_0066: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
42 bool,
43 bool,
44 string,
45 class [mscorlib]System.Type,
46 class [mscorlib]System.Type[])
47 IL_006b: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
48 IL_0070: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
49 IL_0075: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
50 IL_007a: ldfld      !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Target
51 IL_007f: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site2'
52 IL_0084: ldloc.0
53 IL_0085: callvirt instance void class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>::Invoke(!0,
54                                                                                                                                             !1)
55 IL_008a: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site3'
56 IL_008f: brtrue.s   IL_00b7
57 IL_0091: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance()
58 IL_0096: ldc.i4.0
59 IL_0097: ldc.i4.0
60 IL_0098: ldstr "KissFanweixiao"
61 IL_009d: ldtoken    [mscorlib]System.Object
62 IL_00a2: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
63 IL_00a7: ldnull
64 IL_00a8: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder,
65 bool,
66 bool,
67 string,
68 class [mscorlib]System.Type,
69 class [mscorlib]System.Type[])
70 IL_00ad: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder)
71 IL_00b2: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site3'
72 IL_00b7: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site3'
73 IL_00bc: ldfld      !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>>::Target
74 IL_00c1: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>> LearnCSharp4.Program/'<Main>o__SiteContainer0'::'<>p__Site3'
75 IL_00c6: ldloc.0
76 IL_00c7: callvirt instance void class [System.Core]System.Action`2<class [System.Core]System.Scripting.Actions.CallSite,object>::Invoke(!0,
77                                                                                                                                             !1)
78 IL_00cc: ret
79} // end of method Program::Main

如果不喜欢看MSIL的话可以这样看:

Code
1private static void Main(string[] args)
2{
3 object foo1 = new Foo();
4 if (<Main>o__SiteContainer0.<>p__Site1 == null)
5 {
6 <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "Do1", typeof(object), null));
7    }
8 <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo1);
9 if (<Main>o__SiteContainer0.<>p__Site2 == null)
10 {
11 <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "Do2", typeof(object), null));
12    }
13 <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, foo1);
14 if (<Main>o__SiteContainer0.<>p__Site3 == null)
15 {
16 <Main>o__SiteContainer0.<>p__Site3 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "KissFanweixiao", typeof(object), null));
17    }
18 <Main>o__SiteContainer0.<>p__Site3.Target(<Main>o__SiteContainer0.<>p__Site3, foo1);
19}

本文旨在为不方便下载和使用vs2010虚拟机朋友帮个忙.

(在后台编辑的时候报stack overflow的问题...无法提交...)

c#4.0新特性之一: Dynamic Lookup (2)

posted @ 2008-10-29 22:17 new 维生素C.net() 阅读(1956) 评论(22) 编辑 收藏

 回复 引用 查看   
#1楼 2008-10-29 22:40 地狱门神      
C#和VB越来越像了。
 回复 引用 查看   
#2楼 2008-10-29 22:45 包建强      
open day的时候我就和那个C# Team的老外讨论这件事,C#越搞越复杂,失去了原有的简洁,越来越像解释型语言。总之除了泛型,所有在编译器里面添加的语法糖,我都臭骂了一通,包括LINQ和匿名方法。
老外无语,只好和我谈起了Scott、DNN。

 回复 引用 查看   
#3楼 2008-10-29 23:19 Justin      
我想,这次C#4.0特别强调跟VB.NET的彼此取长补短,应该是微软考虑到目前VB的用户群还比较庞大,为了争取这部分忠实的用户,让他们逐渐过渡到C#上来,可能到C#6.0或更远的时候,VB就不会再支持了,那时候VB就是C#了~
 回复 引用 查看   
#4楼 2008-10-30 00:11 木野狐(Neil Chen)      
@Justin
我觉得这个说法不太正确。其实动态特性的加入是在开发 IronPython 的过程中提炼出了 DLR,然后权衡觉得在 C# 里加入动态性很有用。
至于 VB,虽然有统一这两个语言特性的趋势,但是 VB 的语法不会改变的,VB不再支持我觉得不可能发生。

 回复 引用   
#5楼 2008-10-30 00:17 Hengaoxiao[未注册用户]
这个功能很好.很早就希望C#直接支持LATEBINDING了.

事实上这个功能用于与COM交互更有用.

这样就不需要在不同版本的接口中切换来切换去了.

 回复 引用 查看   
#6楼[楼主] 2008-10-30 00:21 new 维生素C.net()      
@地狱门神
各取所长嘛

 回复 引用 查看   
#7楼[楼主] 2008-10-30 00:24 new 维生素C.net()      
@包建强
是那个白发老头吧? 我总看他拿出他的本本写两行code然后就再合上,也不知道在干什么...
不过反过来看,乱七八糟加了这么一堆东西,开发起来是方便了不少,但是这c#产品线拉的也太长了...
不过,dynamic的确是个好东西!

 回复 引用 查看   
#8楼[楼主] 2008-10-30 00:25 new 维生素C.net()      
@Justin
我觉得不能这么说. 动态语言是一种趋势,函数编程是一个思想,C#走上这条道路应该是个好事,是种revolution.

 回复 引用 查看   
#9楼[楼主] 2008-10-30 00:27 new 维生素C.net()      
@木野狐(Neil Chen)
严重同意. vb是个经典. basic语言是个绝对的传奇. 我就挺喜欢那个给iPhone做basic解释器的家伙 :)

 回复 引用   
#10楼 2008-10-30 00:58 看不惯[未注册用户]
@包建强
人家老外是碰上你这么个愣头青懒得鸟你,还真以为自己是个人了;你牛逼就自己写C/C++去啊,在这里JJYY夸海口算什么本事

 回复 引用   
#11楼 2008-10-30 02:56 Ian[未注册用户]
dynamic只是在开发的时候方便了 但对于clr的改变还不大 不过在.net4.0的时候 这个产品已经成熟了
 回复 引用 查看   
#12楼 2008-10-30 08:50 Justin      
@new 维生素C.net()
@木野狐(Neil Chen)
我是从微软的战略发展来考虑这个问题的(当然也是瞎猜的),说VB短时间内不再支持了可能性肯定不大,但是像XP一样,当VB的特性完全可以用c#替代的时候,微软完全停止销售正版的VB,只对之前卖出的版本在有效服务器内进行技术支持,这样逐步把大部分原来的VB用户群里的大部分发展到C#上来,让他们继续忠实于微软最新的主打产品。
我还记得那天听Charlie Calvert讲课时,他的PPT里一开始就有一张非常温馨的两个双胞胎女孩的照片,当时给我的就是这个感觉。

 回复 引用   
#13楼 2008-10-30 09:24 dali[未注册用户]
我想说的是, 如果有智商低的人看不惯dynamic、LINQ和匿名方法等等,大可以不用,C#2.0提供功能已经足够干活的了
 回复 引用 查看   
#14楼 2008-10-30 10:12 Ivony...      
对于上面的某些言论深不以为然。

语言只是一个工具,工具用的好不好主要还是看使用的人。就算是C的有限特性也足以写出一个令人崩溃的代码出来。

 回复 引用   
#15楼 2008-10-30 10:33 Xjog[未注册用户]
c#是Win32时代的VB6,我就这么理解的
说C#是C++的后代是骗人的

 回复 引用 查看   
#16楼[楼主] 2008-10-30 10:55 new 维生素C.net()      
@dali
对!

 回复 引用 查看   
#17楼 2008-10-30 12:44 BZZ      
C# N 年后会不会灭亡???
 回复 引用 查看   
#18楼 2008-10-30 13:02 天 天      
@BZZ
我想会的,哈哈

很多人都说java越来越大,已经失去了很多拥护者,好像c#也有这样的命运

 回复 引用 查看   
#19楼 2008-10-30 14:36 装配脑袋      
@Justin

难道VB新版本就不再加新功能了吗?动态这东西可是VB10年前就支持的。你看看VB现在支持的XML集成,那种功能等C#有的时候我早就用它开发无数东西出来了。我现在就要用它开发一种东西,你甘心等10年后吗;)。10年后VB又有更多新特性被我玩于掌故之间。

 回复 引用 查看   
#20楼 2008-10-30 14:41 装配脑袋      
我2005年就用VB的动态特性辅助了泛型的开发,明显开阔了思路。VBF对泛型的应用现在看仍然是高水准的吧。而且动态语言如今已经发展到什么地步了仅仅从支持dynamic关键字也是看不到的。
所以不要觉得C#未来某个版本可以一统天下,要眼光开阔一些。

 回复 引用 查看   
#21楼 2008-10-30 23:18 Anytao      
比俺先了一步,支持先。其他,就不说啥了:-)
 回复 引用   
#22楼 2008-11-14 16:07 路过的;;[未注册用户]
LZ的硬盘很强大!