C#中的Object、Dynamic与var的区别

让我们快速看看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中动态创建的对象

你可能感兴趣的:(C#,object,c#,编译器,string,expandoobject,存储)