[c#]object&var&dynamic的区别[转]
让我们快速看看object关键字先。我不会对它讲太多,因为它在C#1.0就已经有了。这个关键字没有更多东西除了作为System.Object的快捷方式,System.Object是C#类层次的根类型。(然而,正如Eric Lippert在他博客中指出,并非一切类型都继承源于object[中][英])这是一个强大的机制,这样你几乎可以分配任何实例值到此类型。
这是个小例子用来演示使用object关键字的好处和问题。
object obj = 10;
Console.WriteLine(obj.GetType());
// 输出 System.Int32 因为
// 这是这个对象里存储的值的类型。
// 一个编译错误, 因为
// 在编译时obj的类型是System.Object。
// obj = obj + 10;
// 你需要显式转换obj到期望的类型。
obj = (int)obj + 10;
// 然而, 这样并不表示你真的安全。
// 你可能转换到一个错误的类型
// 而编译器并不会发现。
// 这里你会在运行时得到一个异常,
// 因为obj是int类型,不是string类型。
// obj = (string)obj + 10;
// 如果你将其转换到一个错误的数值类型,
// 你也会得到一个运行时异常
// 虽然有隐式转换的语法在这里。
// obj = (double)obj + 10;
你可以看到,尽管obj存储一个int值,如果没有转换,编译器仍然不会让你执行任何数学操作。它看起来好像是帮助你确定是否你真有一个int类型,但它不是。你可以转换到一个完全不同的类型但编译器却检查不出。结果便是,你得到一个运行时异常。
所以你不得不执行显示转换,这并不保证什么,仅仅因为如果不转换,编译器不会让你运行。
新的dynamic关键字来了。它告诉编译器不要强制附加规则在你的代码上。
dynamic dyn = 10;
Console.WriteLine(dyn.GetType());
// 跟"object"一样.
// 输出 System.Int32 因为
// 这是这个对象里存储的值的类型。
// 没有编译错误,因为
// 编译器不会在编译时
// 试图识别dynamic对象的类型。
dyn = dyn + 10;
// 同样,这个操作会在所有数值或
// 其他支持“+”操作符的类型上成功运算。
dyn = 10.0;
dyn = dyn + 10;
dyn = "10";
dyn = dyn + 10;
这是一个object和dynamic间的主要区别----用dynamic相当于你告诉编译器此对象的类型只在运行时确定,编译器不会试图干预。最终,你可以写更少的代码。但我要强调一下,相对于使用原有的object关键字,这样做不会增加任何危险。同样,也不会减少任何危险,所以当操作任何对象需要类型检查技术时(如反射),则使用dynamic对象会更好。
接下来通常会出现如下问题:“既然dynamic对象可以是任意对象并且编译器不会检查它是什么,这是否意味着你可以传递一个dynamic对象给我的信任方法/系统并使它崩溃?”
让我们假设拥有一个简单方法。
public static void Print(string arg)
{
Console.WriteLine(arg);
}
现在我们看看你能怎么传递dynamic对象给它。
dynamic dyn = 10;
// 你将于运行时在这里得到一个异常.
Print(dyn);
你可以看见,虽然编译器允许你传递一个dynamic对象给你的方法,但你的方法永远不会得到这个对象如果它是个错误的类型。在方法实际执行之前已经有一个异常抛出。只有当它包含一个恰当值时,你才能传递dynamic对象给你的方法,在这里是string类型。
dynamic dyn = "10";
Print(dyn);
再次强调,这跟你使用object关键字相比不会有太多行为上的区别。
// 不会编译通过.
//Print(obj);
// 编译, 但这里在运行时会有个异常。
//Print((string)obj);
// 这里的代码会正常工作,因为现在obj是个string型,
// 但你不需要转换。
obj = "10";
Print((string)obj);
有人说(int)obj这种写法并不难理解,为什么要再来个dynamic呢?好吧,有些情况下你不得不执行较多的转换操作,这使得你的代码很难理解。还有些情况下简单的转换并不能达到目的,你需要调用反射方法,比如InvokeMember或GetProperties。一个很好的例子便是COM互操作,这就是为什么它要修改以使用新dynamic特性(更多信息点这里“how-to”)。
同时,用dynamic关键字和dynamic语言运行时让很多以前不可能实现或难以实现的方案变得可行,包括与动态语言互操作。我在之前的博客里强调过这两个情形: Introducing ExpandoObject 和Creating Wrappers with DynamicObject.
你可能想去MSDN walkthrough看看,它展示你该怎么从C#和Visual Basic调用IronPython库,一个真的很酷的介绍,Using Dynamic Languages to Build Scriptable Applications,作者Dino Viehland。另一个好的介绍展示一些例子并解释设计原则背后是这个特性Dynamic Binding in C# 4,作者Mads Torgersen。
结论是我们不需要担心会有人能用dynamic特性破坏你的代码。它没有更多(同时,也没有更少)的危险相对于object关键字。
所以,如果你经常使用object关键字并且不得不执行一堆转换"并且/或者"使用反射来调用对象的方法或属性,你可能应该留意一下dynamic关键字。在很多时候它比object更方便并且编写更少的代码。
原文没有提到var关键字,相信很多人会在这时想到var。
var在C#3.0中产生,其在MSDN中的定义:在方法范围中声明的变量可以具有隐式类型 var。隐式类型的本地变量是强类型变量(就好像您已经声明该类型一样),但由编译器确定类型。
使用var关键字申明变量,编译器会根据分配的实例类型确定此变量的类型,即类型推断。
var va = 10;
Console.WriteLine(va.GetType());
// 输出 System.Int32 因为
// 这是这个对象里存储的值的类型。
// 而va的编译时类型也已经被编译器确定为int型。
// 没有编译错误,因为
// va在第一句赋值时
// 便被编译器确定其类型是int。
va = va + 10;
// 编译错误,因为
// va变量已经确定是int型,而10.0是double型,
// 且double型向int型不可隐式转换。
// va = 10.0;
跟object比,var属于强类型;
跟dynamic比,var并无任何运行时的动态特性,仅仅是编译器在编译时反向推断其类型,同直接用其编译时类型声明变量功能上是一样的。它仅仅是编译器提供的语法糖。dynamic可不是语法糖,dynamic是需要运行时支持的。
VAR 是3.5新出的一个定义变量的类型
其实也就是弱化类型的定义
VAR可代替任何类型
编译器会根据上下文来判断你到底是想用什么类型的
至于什么情况下用到VAR 我想就是你无法确定自己将用的是什么类型
就可以使用VAR 类似 OBJECT
但是效率比OBJECT高点
使用var定义变量时有以下四个特点:
1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式:
var s;
s = “abcd”;
2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。
3. var要求是局部变量。
4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。
var表示“变量的类型是在编译时决定的”,但是dynamic表示“变量的类型是在运行时决定的”。因此,dynamic与var具有截然不同的含义。
var让你在初始化变量时少输入一些字,编译器会根据右值来推断出变量的类型。dynamic更厉害,它告诉编译器,根本就别理究竟是啥类型,运行时再推断不迟。
var只能用于局部变量的定义,你不能把类的属性定义成 var,也不能把方法的返回值类型或者是参数类型定义成var。dynamic就没有这些局限了。
dynamic类型并没有跳过类型校验,只是延迟到了运行时。如果在运行时,检测到类型不兼容,照样会抛出异常。
你可能在以下情况下使用dynamic:
1.COM对象
2.动态语言(如IronPython,IronRuby等)对象
3.反射对象
4.C# 4.0中动态创建的对象