Jun 30

VB2010和C#4.0后期绑定的实现比较

昨天EP研究了一下C#4.0和VB2010的后期绑定实现,弄了两个简单的测试程序,反编译出来代码如下:

   1: <STAThread> _

   2: Public Shared Sub Main()

   3:     Dim o As Object = New ExpandoObject

   4:     NewLateBinding.LateCall(o, Nothing, "Test", New Object(0  - 1) {}, Nothing, Nothing, Nothing, True)

   5:     NewLateBinding.LateSet(o, Nothing, "NewMember", New Object() { 1 }, Nothing, Nothing)

   6: End Sub

   1: private static void Main(string[] args)

   2: {

   3:     object o = new ExpandoObject();

   4:     if (<Main>o__SiteContainer0.<>p__Site1 == null)

   5:     {

   6:         <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Test", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));

   7:     }

   8:     <Main>o__SiteContainer0.<>p__Site1.Target.Invoke(<Main>o__SiteContainer0.<>p__Site1, o);

   9:     if (<Main>o__SiteContainer0.<>p__Site2 == null)

  10:     {

  11:         <Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "NewMember", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));

  12:     }

  13:     <Main>o__SiteContainer0.<>p__Site2.Target.Invoke(<Main>o__SiteContainer0.<>p__Site2, o, 1);

  14: }

C#会给调用生成一个CallSite缓存起来,然后后续调用全部是通过这个CallSite。VB 则是全部通过 NewLateBinding 来调用,而且内部每次调用都会新建一个CallSite。

开始还以为M$脑子进水了,VB的后期绑定这样弄性能得差到一个什么程度。后来跟进CallSite的代码,发现其实CallSite里面也是有缓存机制的,内部的Delegate不会重复建立,这样的话虽然VB的性能会比C#稍差,应该也不到不能接受的地步。

这么做应该是为了兼容旧的VB6后期绑定,从代码也可以看出来,如果对象没有实现IDynamicMetaObjectProvider,就会用旧版的后期绑定方式。

这样保持兼容性感觉很不方便,VB.NET相比VB6的一大改进是Option Strict,之前写代码的时候一直开着,能减少很多编码错误,其中包括误用后期绑定。而如果要使用新的ExpendoObject和其它动态功能的话,又不能启用这个检查。

另外VB不像C#新增了dynamic关键字,要使用后期绑定得把变量声明为Object类型,使代码更容易出错。

结论:在后期绑定这方面,VB本来是前辈,不过现在反而被C#赶上了。。。该说是VB的又一个悲剧么。。。

(.NET内部的反编译代码就不贴了,各位有兴趣可以用Reflector研究一下)

——————————————–

7月11日更新:

早上突然发现上次的推理有个大漏洞,VB的NewLateBinding只会对IDynamicMetaObjectProvider使用CallSite,一般对象还是会使用老版的反射调用。这样一来多次调用的性能就远远比不上C#了。。。