直接投射vs'as'运算符?

时间:2008-09-25 10:09:19

标签: c# casting

请考虑以下代码:

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
}

三种类型的铸造之间有什么区别(好吧,第三种不是铸造,但你得到了意图)。应该首选哪一个?

17 个答案:

答案 0 :(得分:773)

string s = (string)o; // 1

如果o不是string,则会InvalidCastException。否则,即使os,也会将o分配给null

string s = o as string; // 2

如果null不是sostring,则o会分配给null。因此,您不能将它与值类型一起使用(在这种情况下,运算符永远不会返回null)。否则,将o分配给s

string s = o.ToString(); // 3

如果onull,则会导致NullReferenceException。无论o.ToString()是什么类型,都会向s分配o次返回。


使用1进行大多数转换 - 它简单明了。我倾向于几乎从不使用2,因为如果某些东西不是正确的类型,我通常会发生异常。我只看到需要这种返回null类型的功能,其中设计糟糕的库使用错误代码(例如,返回null =错误,而不是使用异常)。

3不是强制转换,只是一个方法调用。当您需要非字符串对象的字符串表示时使用它。

答案 1 :(得分:322)

  1. string s = (string)o;在应有的时候使用 绝对是另一回事。
  2. string s = o as string;可能是另一个时使用 事情。
  3. string s = o.ToString();当你不在乎什么时使用 它只是你想用的 可用的字符串表示。

答案 2 :(得分:26)

这实际取决于你是否知道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方法,则第三种方法可能没问题可能会做你想做的事。

答案 3 :(得分:9)

如果你已经知道它可以投射到什么类型,请使用C风格的演员:

var o = (string) iKnowThisIsAString; 

请注意,只有使用C风格的演员才能执行显式类型强制。

如果您不知道它是否是所需类型,并且您将使用它,请使用作为关键字:

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

请注意, as 不会调用任何类型转换运算符。如果对象不为null并且本身具有指定类型,则它将仅为非null。

使用ToString()获取任何对象的人类可读字符串表示形式,即使它不能转换为字符串。

答案 4 :(得分:7)

当您使用FindControl方法时,as.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;
}

这不是一件大事,但它保存了代码行和变量赋值,而且它更具可读性

答案 5 :(得分:6)

'as'基于'is',这是一个关键字,在运行时检查对象是否是polimorphycally兼容的(基本上如果可以进行强制转换),如果检查失败则返回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;

在这些情况下,你必须使用铸造。

答案 6 :(得分:5)

2对于转换为派生类型非常有用。

假设 a 是动物:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

将获得 a 以最少的演员阵容。

答案 7 :(得分:4)

“(string)o”将导致InvalidCastException,因为没有直接强制转换。

“o as string”将导致s为空引用,而不是抛出异常。

“o.ToString()”不是任何类型的演员,它是一个由对象实现的方法,因此以某种方式由.net中的每个类实现“做某事”它被调用的类的实例并返回一个字符串。

不要忘记,为了转换为字符串,还有Convert.ToString(someType instanceOfThatType),其中someType是一组类型之一,基本上是框架基类型。

答案 8 :(得分:4)

根据此页面上的实验运行:http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(这个页面有时出现一些“非法推荐人”错误,所以只要刷新就可以了)

结论是,“as”运算符通常比强制转换更快。有时快几倍,有时只是快一点。

我在peronsonally事情“as”也更具可读性。

所以,既然它既快又“更安全”(不会抛出异常),并且可能更容易阅读,我建议一直使用“as”。

答案 9 :(得分:3)

string s = o as string; // 2

是首选,因为它避免了双重铸造的性能损失。

答案 10 :(得分:3)

所有给出的答案都很好,如果我可以添加一些东西: 要直接使用字符串的方法和属性(例如ToLower),您不能写:

(string)o.ToLower(); // won't compile

你只能写:

((string)o).ToLower();

但你可以写一下:

(o as string).ToLower();

as选项更具可读性(至少在我看来)。

答案 11 :(得分:3)

看来他们俩在概念上是不同的。

直接投射

类型不必严格关联。它有各种口味。

  • 自定义隐式/显式转换:通常会创建一个新对象。
  • 值类型隐含:复制而不会丢失信息。
  • 值类型明确:复制和信息可能会丢失。
  • IS-A关系:更改引用类型,否则抛出异常。
  • 相同类型:'投射是多余的'。

感觉对象将被转换为其他东西。

AS运营商

类型有直接关系。如:

  • 参考类型: IS-A关系对象始终相同,只是参考更改。
  • 值类型:复制装箱和可空类型。

感觉就像你要以不同的方式处理对象。

样本和IL

TextBlock

答案 12 :(得分:1)

我想引起注意 as 运算符的以下细节:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

  

请注意,as运算符仅执行参考转换,   可为空的转换和装箱转换。 as运算符不能   执行其他转换,例如用户定义的转换,   而是应使用强制转换表达式执行。

答案 13 :(得分:0)

当试图获取任何可能为null的任何东西(任何类型)的字符串表示时,我更喜欢下面的代码行。它是紧凑的,它调用ToString(),并正确处理空值。如果o为null,则s将包含String.Empty。

String s = String.Concat(o);

答案 14 :(得分:0)

由于没有人提及它,因此最接近于关键字的instanceOf到Java:

obj.GetType().IsInstanceOfType(otherObj)

答案 15 :(得分:0)

如果您的应用string s = (string) o;的逻辑上下文是唯一有效的类型,请使用直接投射string。通过这种方法,您将获得InvalidCastException并实施Fail-fast的原则。如果使用as运算符,将保护您的逻辑不会进一步传递无效类型或获取NullReferenceException。

如果逻辑需要多个不同类型的投射string s = o as string;并在null上检查或使用is运算符。

新的酷炫功能已经出现在C#7.0中,以简化演员和检查是Pattern matching

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }

答案 16 :(得分:0)

C#支持以下两种类型的转换(强制转换):

|

(C)v

•将给定表达式

中的静态类型v转换为c

•仅当v的动态类型为c或c

的子类型时才可能

•如果不是,则抛出InvalidCastException

|

v为C

•(c)v

的非致命变体

•因此,将静态类型v转换为给定表达式中的c

•如果v的动态类型不是c或c

的子类型,则返回null