直接铸造与" as"运算符?
考虑以下代码:
void Handler(object o, EventArgs e) { // I swear o is a string string s = (string)o; // 1 //-OR- string s = o as string; // 2 // -OR- string s = o.ToString(); // 3 }
三种类型的转换之间有什么区别(好吧,第三个类型不是转换,但我们有意图)。哪一个应该是首选?
解决方案
" as"基于" is",这是一个关键字,它在运行时检查对象是否在形态上兼容(基本上可以进行强制转换),如果检查失败,则返回null。
这两个是等效的:
使用'as':
string s = o as string;
使用"是":
if(o is string) s = o; else s = null;
相反,c样式强制转换也是在运行时进行的,但是如果无法进行强制转换,则会引发异常。
仅添加一个重要事实:
" as"关键字仅适用于引用类型。我们不能执行以下操作:
// I swear i is an int int number = i as int;
在这种情况下,我们必须使用投射。
string s = (string)o; // 1
如果o不是字符串,则抛出InvalidCastException。否则,即使o为null,也将o分配给s。
string s = o as string; // 2
如果o'不是
string或者
o为
null,则将
null分配给
s。因此,我们不能将其用于值类型(在这种情况下,运算符永远不会返回" null")。否则,将
o分配给
s`。
string s = o.ToString(); // 3
如果o为null,则导致NullReferenceException。不管o是什么类型,都将返回的o.ToString()返回给s。
使用1进行大多数转换非常简单明了。我倾向于几乎从不使用2,因为如果某些类型不正确,我通常希望发生异常。我只看到了这种带有设计错误的库的返回null类型的功能的需要,这些库使用错误代码(例如return null = error,而不是使用异常)。
3不是强制转换,而只是方法调用。在需要非字符串对象的字符串表示形式时使用它。
这实际上取决于我们是否知道o是否是字符串以及我们要如何处理。如果评论表示o
实际上确实是一个字符串,则我希望直接使用(string)o
强制转换,它不太可能失败。
使用直接强制转换的最大优点是,当它失败时,我们将收到InvalidCastException,它可以告诉我们几乎所有的错误。
使用as运算符时,如果o不是字符串,则将s设置为null,如果不确定并想要测试s,则非常方便:
string s = o as string; if ( s == null ) { // well that's not good! gotoPlanB(); }
但是,如果不执行该测试,则稍后将使用s
并抛出NullReferenceException。一旦它们在野外发生,它们往往更常见且更难追踪,因为几乎每一行都取消引用一个变量并可能抛出一个变量。另一方面,如果我们要强制转换为值类型(任何原始类型或者诸如DateTime之类的结构),则必须使用直接强制转换,因为" as"将不起作用。
在转换为字符串的特殊情况下,每个对象都有一个" ToString",因此,如果" o"不为空,并且我们认为" ToString"方法可能会做我们想要的事情,那么第三个方法就可以了。
"(string)o"将导致InvalidCastException,因为没有直接强制转换。
" o as string"将导致s为空引用,而不是引发异常。
" o.ToString()"本身不是任何形式的转换,它是一种由对象实现的方法,因此由.net中的每个类以某种方式对.NET实例进行"处理"调用它的类,并返回一个字符串。
不要忘记,要转换为字符串,还有Convert.ToString(someType instanceOfThatType),其中someType是一组类型之一,本质上是框架的基本类型。
- 绝对应该是另一回事时使用。
- 当某件事可能是另一回事时使用。
- 当我们不在乎它是什么但只想使用可用的字符串表示形式时使用。
2对于强制转换为派生类型很有用。
假设a是动物:
b = a as Badger; c = a as Cow; if (b != null) b.EatSnails(); else if (c != null) c.EatGrass();
用最少的石膏就够了。
如果我们已经知道它可以强制转换为哪种类型,请使用C样式强制转换:
var o = (string) iKnowThisIsAString;
请注意,只有使用C样式强制转换,我们才能执行显式类型强制。
如果我们不知道它是否是所需的类型,并且要使用它,请使用as关键字:
var s = o as string; if (s != null) return s.Replace("_","-"); //or for early return: if (s==null) return;
请注意,as不会调用任何类型转换运算符。仅当对象不为null且本身是指定类型的对象时,它才会为非null。
使用ToString()获取任何对象的人类可读的字符串表示形式,即使它不能转换为字符串也是如此。
string s = o as string; // 2
首选,因为它避免了双重浇铸的性能损失。
当我们使用FindControl方法时,在asp.net中使用as关键字是很好的。
Hyperlink link = this.FindControl("linkid") as Hyperlink; if (link != null) { ... }
这意味着我们可以对类型化的变量进行操作,而不必像直接强制转换那样从object
中进行强制转换:
object linkObj = this.FindControl("linkid"); if (link != null) { Hyperlink link = (Hyperlink)linkObj; }
这不是一件大事,但它节省了代码行和变量赋值,而且可读性强
根据在此页面上运行的实验:http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(此页面有时会显示一些"非法引荐来源网址"错误,因此请刷新一下)
结论是," as"运算符通常比强制转换更快。有时快很多倍,有时快一点。
从我的角度来看," as"这个东西也更具可读性。
因此,由于它既更快又更"安全"(不会抛出异常),并且可能更易于阅读,所以我建议始终使用" as"。