分享

C# Dynamic 与Var关键字 简析

 zww_blog 2014-02-26

var表示“变量的类型是在编译时决定的”,但是dynamic表示“变量的类型是在运行时决定的”。因此,dynamic与var具有截然不同的含义。

  var让你在初始化变量时少输入一些字,编译器会根据右值来推断出变量的类型。dynamic更厉害,它告诉编译器,根本就别理究竟是啥类型,运行时再推断不迟。

  var只能用于局部变量的定义,你不能把类的属性定义成 var,也不能把方法的返回值类型或者是参数类型定义成var。dynamic就没有这些局限了。

  dynamic类型并没有跳过类型校验,只是延迟到了运行时。如果在运行时,检测到类型不兼容,照样会抛出异常。

  你可能在以下情况下使用dynamic:

  1.COM对象

  2.动态语言(如IronPython,IronRuby等)对象

  3.反射对象

  4.C# 4.0中动态创建的对象




C#是一种类型安全的编程语言(所有表达式都能解析成某个类型的实例,在编译器生成的代码中,只会执行对这个类型有效的操作),和非类型安全的语言相比,类型安全的优势就体现出来了:

1.许多错误能在编译时检测到,取保代码在执行它之前是正确的。

2.编译时语言通常能生成更小,更快的代码。(在编译时进行更多的假设,并在IL和元数据中落实那些假设) 为了方便开发人员使用反射或者与基本组件通信,dynamic诞生了!
一下代码展示了如何利用反射在一个String目标("根据我找类型")上调用一个方法(“Contains”),向它传递一个实参(“我只是一个string参数”),并将结果存储到局部变量result中。

 static void Main()        {           
     object target = "根据我找类型";           
    object arg = "我只是个string参数";           
    Type[] argtype = new Type[] { arg.GetType()};           
    System.Reflection.MethodInfo method = target.GetType().GetMethod("Contains", argtype);           
    object[] argm = new object[] { arg};           
    Boolean result=Convert.ToBoolean(method.Invoke(target,argm));

现在,有了dynamic! 

static void Main()       
{           
   dynamic target = "根据我找类型";           
   dynamic arg = "参数";           
   Boolean result = target.Contains(arg);
}
是不是发现有了显著的简化。
 static void Main()       
{           
    Application excel = new Application();           
    excel.Visible = true;           
    excel.Workbooks.Add(Type.Missing);           
   ((Range)excel.Cells[1, 1]).Value = "放入单元格的字符";//如果没有dynamic类型,excel.Cells[1,1]的返回值是objec类型,必须先把它转换为Rang类型才能访问Value属性。           
   excel.Cells[1, 1].Value = "放入单元格的字符";//为COM对象生成一个可由“运行时”调用的包装程序集时,COM方法中使用的任何variant实际都会被转换为dynamic,这称为动态化(dynamicfication)。            //所以这里excel.Cells[1,1]是dynamic类型,可以不必显示把它转换成Range类型也能访问它的Value。动态化显著简化了与COM对象的互操作。       
}

 看到了dynamic的神奇,那再让我们刨根问底吧。

我们可以用dynamic表达式或变量调用一个成员,比如字段,属性/索引器,方法,委托,以及一元/二元/转换操作符,当我们的代码使用dynamic表达式或变量调用一个成员时,编译器会生成特殊的IL代码来描述所需的操作。

这种特殊的代码称为payload(有效载荷)(这些payload代码使用了一个称为运行时绑定器(runtime binder)的类),在运行时,payload代码根据当前由dynamic表达式/变量引用的对象的实际类型来决定具体的操作。

看这个例子:

 static void Main()       
{           

    for (int i = 0; i < 2; i++)           
  {               
     dynamic arg = (i == 0) ? (dynamic)10 : "A";               
     dynamic result = plus(arg);//第一次循环i==0 ,arg=10;所以调用plus时,返回的是int类型。第二次是string类型。               
     M(result);//payload代码判断出传给M的值的实际类型,然后调用相应的重载方法。           
   }         
     Console.ReadKey();       
}       
static dynamic plus(dynamic arg)
  {
    return arg+arg;
  }       
static  void M(int n)
 {
    Console.WriteLine("M(int):{0}", n);
 }      
static void M(string s)
{
    Console.WriteLine("M(string):{0}", s);
 }  

在字段类型,方法参数类型或方法类型被指定为dynamic的前提下,编译器会将这个类型转换为System.Object,并在元数据中向字段,参数或者返回类型应用System.Runtime.CompilerSevices.DynamicAttribute的一个实例。如果是一个局部变量被指定为dynamic,变量类型也会成为Object,但不会向局部变量应用DynamicAttribute,应为它的使用限制在方法之内。

由于dynamic就是object 所以不仅仅将dynamic变成object,或者object变成dynamic就获取两个不同的方法签名。例子:

  object dd(dynamic i)
 {
     return i;
 }
dynamic dd( object i)
 {
     return i;
}

这就通不过编译。

 

dynamic的类型转换:
 static void Main()       
 {           
       object o = 123;//(装箱)           
       Int32 n = o;//错误!不允许从object到int32的隐式转换。           
       Int32 n1 = (Int32)o;//从object显示转换到int32。(拆箱)           
       dynamic od = 123;//(装箱)            
       dynamic os = "dsfsdf";           
       Int32 ns = os;//运行时报错。           
       Int32 nd = od;//从dynamic隐式转换为int32(拆箱)           
      //在本例中可看出,dynamic转为其他类型时,允许省略显示转型。           
      //但是CLR会在运行时验证转型,确保类型安全。如果对象类型不兼容要转换成的类型,clr就会抛出一个InvalidCastException异常。       
  }
这里还有个例子说明dynamic的特性:
 
    void Main(string[] args)
{
    dynamic dyn=123;
    Console.WriteLine(dyn.GetType());
    Console.WriteLine(dyn);
 
     dyn="abc";
    Console.WriteLine(dyn.GetType());
    Console.WriteLine(dyn);
}
结果: System.Int32
            123
            System.String
            abc

dynamic和var的区别:

 

1.var声明一个局部变量只是一种简化语法,它要求编译器根据一个表达式推断具体的数据类型。

 

2.var只能用于声明方法内部的局部变量,而dynamic可用于局部变量,字段,参数。

 

3.表达式不能转型为var,但能转型为dynamic。

 

4.必须显式初始化用var声明的变量,但无需初始化用dynam声明的变量。

 

使用dynamic应注意:

 

在运行时,Microsoft.Csharp.dll必须加载到AppDomain中,这回损害程序性能,并增大内错耗用,Microsoft.Csharp.dll还会加载System.dll和System.Core.dll,如果使用dynamic与COM组件互操作,还会加载System.Dynamic.dll,payload代码执行时会在运行时生成动态代码。这些代码会进入一个驻留在内存的程序集,称为“匿名寄宿的DynamicMethods程序集”(Anonymously Hosted DynamicMethods Assembly).

 

当一个特性的调用使用具有相同运行时类型的dynamic实参发出了大量调用时,这个代码可以增强调度的性能。

 

虽然dynamic能简化语法,但是动态求值功能产生的额外开销也是不容忽视的,毕竟加载所有这些程序集以及额外的内存消耗,会对性能产生额外的影响。如果程序中只是一两个地方需要动态行为,或许传统的做法会更加高效

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多