直接铸造与" as"运算符?

时间:2020-03-06 14:42:47  来源:igfitidea点击:

考虑以下代码:

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或者onull,则将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"。