C#4.0初探:dynamic 关键字

C#新增了dynamic关键字,正因为这一个小小的关键字,C#动态特性向前迈进了一大步。
dynamic是一个类型关键字,声明为dynamic的类型与"静态类型"(这里的静态类型是指编译时确定的类型,下同)相比最大的特点它是"动态类型",它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时查找,如果方法存在并且参数正确,会正常调用,否则会抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常。

看一个最简单的示例:
using System; 

namespace  Xianfen.Net.TestDynamic 

     class Program 
    { 
         static  void Main() 
        { 
            dynamic d = Console.Out; 
            dynamic a; 
            a =  new Int32(); 
             int b = a; 
            a++; 
            a--; 

            d.WriteLine( " http://www.xianfen.net/ "); 
            d.WriteLine(d.GetType()); 
            d.writeln( " test ");  // 抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常  
        } 
    } 
}

对dynamic类型的操作只能有以下几种:
·赋值
·方法调用
·自增
·自减
·接受"静态类型"的构造器创建的对象

与关键字var的比较
从表面上看,dynamic与var关键字的用法很像,但实质上有本质区别。
var关键字被称为:隐含类型局部变量(Local Variable Type Inference),var只能用作局部变量,不能用于字段、参数等;声明的同时必须初始化;初始化时类型就已经明确了,并且不能再被赋值不能进行隐式类型转换的类型的数据;编译时编译器会对var定义的变量进行类型推断,这时变量的类型已经被确定。
dynamic可用于类型的字段,方法参数,方法返回值,可用于泛型的类型参数等;可以赋值给或被赋值任何类型并且不需显式的强制类型转换,因为这些是运行时执行的,这要得益于dynamic类型的动态特性。

与反射的比较
首先能看到的是,dynamic与反射相比,执行相同操作所需的代码少的多。
如调用类Me中的GetName()方法。
class Me 

     public  string Blog {  getset; } 

     public  string GetName() 
    { 
         return  " Zhenxing Zhou "
    } 
}

用反射调用GetName()方法:
Assembly a = Assembly.GetExecutingAssembly(); 
object instance = a.CreateInstance( " Xianfen.Net.TestDynamic.Me "); 
Type type = instance.GetType(); 
MethodInfo mi = type.GetMethod( " GetName "); 
object result = mi.Invoke(instance,  null);

同样的dynamic调用:
dynamic myInfo =  new Me(); 
string result = myInfo.GetName();

dynamic类型与反射相比能进行的操作要少的多。
目前dynamic类型对属性调用是不可用的,但我们知道,属性生成IL时,对属性的读或写会生成对应的在属性名前加上get_或set_前缀生成相应的方法,尝试调用两个方法来访问属性:
dynamic myInfo =  new Me(); 
myInfo.set_Blog( " http://www.xianfen.net/ "); 
string result = myInfo.get_Blog();

会抛出异常,提示找不到get/set_Blog方法。这点比较遗憾,同样,对有参属性的访问也是不行的。
反射还可以访问私有方法字段以及其它类型成员及取得类型及类型成员的信息等。

dynamic类型的效率
效率问题应该是大家很关心的,我的感觉:不要对动态语言有很高的效率抱有太大的希望,但另一方面,算法的设计对效率的影响非常大,功能与性能经常存在一个平衡点。
要分析其效率,就要看看编译后内部都干了些啥,方法是写些简单的代码,查看IL。
代码:
using System; 

namespace  Xianfen.Net.TestDynamic 

     class Program 
    { 
         static  void Main() 
        { 
            dynamic d =  " str "
            d.ToString(); 
        } 
    } 
}

对应的IL代码:
.class  private  auto  ansi  beforefieldinit Program 
     extends [mscorlib]System.Object 

     .method  public  hidebysig  specialname  rtspecialname  instance  void .ctor()  cil  managed 
    { 
         .maxstack  8 
         L_0000:  ldarg.0 
         L_0001:  call  instance  void [mscorlib]System.Object::.ctor() 
         L_0006:  ret 
    } 

     .method  private  hidebysig static  void Main()  cil  managed 
    { 
         .entrypoint 
         .maxstack  9 
         .locals  init ( 
            [ 0object d, 
            [ 1class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$ 0$ 0000
         L_0000:  ldstr  " str " 
         L_0005:  stloc.0 
         L_0006:  ldsfld  class [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>> Xianfen.Net.TestDynamic.Program/
o__SiteContainer0::<>p__Site1 
         L_000b:  brtrue.s L_003f 
         L_000d:  ldc.i4.0 
         L_000e:  ldstr  " ToString " 
         L_0013:  ldtoken Xianfen.Net.TestDynamic.Program 
         L_0018:  call  class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
         L_001d:  ldnull 
         L_001e:  ldc.i4.1 
         L_001f:  newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo 
         L_0024:  stloc.1 
         L_0025:  ldloc.1 
         L_0026:  ldc.i4.0 
         L_0027:  ldc.i4.0 
         L_0028:  ldnull 
         L_0029:  newobj  instance  void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,  string
         L_002e:  stelem.ref 
         L_002f:  ldloc.1 
         L_0030:  newobj  instance  void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpCallFlags,  stringclass [mscorlib]System.Type,  class[mscorlib]System.Collections.Generic.IEnumerable` 1< class [mscorlib]System.Type>,  class [mscorlib]System.Collections.Generic.IEnumerable` 1< class[Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>) 
         L_0035:  call  class [System.Core]System.Runtime.CompilerServices.CallSite` 10> [System.Core]System.Runtime.CompilerServices.CallSite` 1< class[mscorlib]System.Action` 2< class [System.Core]System.Runtime.CompilerServices.CallSite,  object>>::Create( class[System.Core]System.Runtime.CompilerServices.CallSiteBinder) 
         L_003a:  stsfld  class [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>> Xianfen.Net.TestDynamic.Program/
o__SiteContainer0::<>p__Site1 
         L_003f:  ldsfld  class [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>> Xianfen.Net.TestDynamic.Program/
o__SiteContainer0::<>p__Site1 
         L_0044:  ldfld ! 0 [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>>::Target 
         L_0049:  ldsfld  class [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>> Xianfen.Net.TestDynamic.Program/
o__SiteContainer0::<>p__Site1 
         L_004e:  ldloc.0 
         L_004f:  callvirt  instance  void [mscorlib]System.Action` 2< class [System.Core]System.Runtime.CompilerServices.CallSite,  object>::Invoke(! 0, ! 1
         L_0054:  ret 
    } 



     .class abstract  auto  ansi sealed  nested  private  beforefieldinit 
o__SiteContainer0 
         extends [mscorlib]System.Object 
    { 
         .custom  instance  void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() 
         .field  public static  class [System.Core]System.Runtime.CompilerServices.CallSite` 1< class [mscorlib]System.Action` 2< class[System.Core]System.Runtime.CompilerServices.CallSite,  object>> <>p__Site1 

    } 
}

可以看出生成的IL代码确实不美观,不过大体能看出端倪。为了方便查看,用Reflector查看,把反编译结果设置为.net2.0,代码清晰多了:
01internal  class Program 
02. { 
03.      //  Methods  
04.      private  static  void Main() 
05.     { 
06.          object d =  " str "
07.          if (
o__SiteContainer0.<>p__Site1 ==  null
08.         { 
09.             
o__SiteContainer0.<>p__Site1 = CallSiteobject>>. 
10. Create( new CSharpInvokeMemberBinder(CSharpCallFlags.None,  " ToString "typeof(Program), 
11nullnew CSharpArgumentInfo[] {  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,  null) })); 
12.         } 
13.         
o__SiteContainer0.<>p__Site1.Target(
o__SiteContainer0.<>p__Site1, d); 
14.     } 
15//  Nested Types  
16.     [CompilerGenerated] 
17.      private  static  class 
o__SiteContainer0 
18.     { 
19.          //  Fields  
20.          public  static CallSiteobject>> <>p__Site1; 
21.     } 
22. }

06行先把赋值给dynamic的值赋给object类型,检查编译器生成的静态类
o__SiteContainer0的静态字段<>p__Site1是否为null,如果是,则对其赋值,赋值的内容在这里不详细研究。然后调用<>p__Site1进行操作。
这里会发现两个问题:赋值给dynamic的值赋给object类型,对于值类型会不会执行同样的操作,会执行装箱操作吗;编译器生成的静态类
o__SiteContainer0的静态字段<>p__Site1应该是缓存作用。这两个问题稍后验证。

1)对值类型进行的操作
如下代码:
using System; 

namespace  Xianfen.Net.TestDynamic 

     class Program 
    { 
         static  void Main() 
        { 
            dynamic d =  5
            d.ToString(); 
        } 
    } 
}

反编译代码:
internal  class Program 

     //  Methods  
     private  static  void Main() 
    { 
         object d =  5
         if (
o__SiteContainer0.<>p__Site1 ==  null
        { 
            
o__SiteContainer0.<>p__Site1 = CallSiteobject>>. 
Create( new CSharpInvokeMemberBinder(CSharpCallFlags.None,  " ToString "typeof(Program), 
nullnew CSharpArgumentInfo[] {  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,  null) })); 
        } 
        
o__SiteContainer0.<>p__Site1.Target(
o__SiteContainer0.<>p__Site1, d); 
    } 

     //  Nested Types  
    [CompilerGenerated] 
     private  static  class 
o__SiteContainer0 
    { 
         //  Fields  
         public  static CallSiteobject>> <>p__Site1; 
    } 
}

可见确实对值类型进行了装箱操作,效率可想而知。

2)编译器生成的缓存类
代码如下:
using System; 

namespace  Xianfen.Net.TestDynamic 

     class Program 
    { 
         static  void Main() 
        { 
            dynamic d =  5
            d.ToString(); 
            d.ToString(); 
        } 
    } 
}

反编译的代码:
internal  class Program 

     //  Methods  
     private  static  void Main() 
    { 
         object d =  5
         if (
o__SiteContainer0.<>p__Site1 ==  null
        { 
            
o__SiteContainer0.<>p__Site1 = CallSiteobject>>.Create( new CSharpInvokeMemberBinder(CSharpCallFlags.None, " ToString "typeof(Program),  nullnew CSharpArgumentInfo[] {  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,  null) })); 
        } 
        
o__SiteContainer0.<>p__Site1.Target(
o__SiteContainer0.<>p__Site1, d); 
         if (
o__SiteContainer0.<>p__Site2 ==  null
        { 
            
o__SiteContainer0.<>p__Site2 = CallSiteobject>>.Create( new CSharpInvokeMemberBinder(CSharpCallFlags.None, " ToString "typeof(Program),  nullnew CSharpArgumentInfo[] {  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,  null) })); 
        } 
        
o__SiteContainer0.<>p__Site2.Target(
o__SiteContainer0.<>p__Site2, d); 
    } 

     //  Nested Types  
    [CompilerGenerated] 
     private  static  class 
o__SiteContainer0 
    { 
         //  Fields  
         public  static CallSiteobject>> <>p__Site1; 
         public  static CallSiteobject>> <>p__Site2; 
    } 
}

代码调用了ToString方法,但编译器生成了两份缓存。

如果是在循环中:
代码:
using System; 

namespace  Xianfen.Net.TestDynamic 

     class Program 
    { 
         static  void Main() 
        { 
            dynamic d =  5

             for ( int i =  0; i <  100; i++) 
            { 
                d.ToString(); 
            } 
        } 
    } 
}

反编译代码:
internal  class Program 

     //  Methods  
     private  static  void Main() 
    { 
         object d =  5
         for ( int i =  0; i <  100; i++) 
        { 
             if (
o__SiteContainer0.<>p__Site1 ==  null
            { 
                
o__SiteContainer0.<>p__Site1 = CallSiteobject>>.Create( new CSharpInvokeMemberBinder(CSharpCallFlags.None, " ToString "typeof(Program),  nullnew CSharpArgumentInfo[] {  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,  null) })); 
            } 
            
o__SiteContainer0.<>p__Site1.Target(
o__SiteContainer0.<>p__Site1, d); 
        } 
    } 

     //  Nested Types  
    [CompilerGenerated] 
     private  static  class 
o__SiteContainer0 
    { 
         //  Fields  
         public  static CallSiteobject>> <>p__Site1; 
    } 
}

可见在循环中,相同的操作做了一次缓存;但非循环环境下,调用一次会缓存一次,猜测原因是,重复调用一个方法的次数不会太多,并且很多情况准确查找起来比较困难。
(以上代码在VS2010Beta1下测试通过)
URL:  http://www.xianfen.net/
Author: Zhenxing Zhou

你可能感兴趣的:(DotNet,dynamic,Net4.0,C#)