直接施放vs'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 } 

三种types的铸造有什么区别(好吧,第三种不是铸造,但你有意图)。 哪一个应该是首选?

 string s = (string)o; // 1 

如果o不是string则引发InvalidCastException 。 否则,将o赋给s ,即使onull

 string s = o as string; // 2 

如果o不是一个string或者onull则将null赋值给s 。 出于这个原因,你不能使用它的值types(运算符在这种情况下永远不能返回null )。 否则,将o赋给s

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

如果onull则会导致NullReferenceException 。 分配任何o.ToString()返回给s ,不pipeo是什么types。


使用1进行大多数转换 – 这很简单直接。 我倾向于几乎从不使用2,因为如果事情不是正确的types,我通常期望发生exception。 我只看到需要使用错误代码(例如,返回null =错误,而不是使用exception)的糟糕的devise库的这种return-nulltypes的function。

3不是一个强制转换,只是一个方法调用。 在需要非string对象的string表示时使用它。

  1. 当一些东西肯定是其他的东西时使用。
  2. 当有事可能是另一回事时使用。
  3. 当你不关心它是什么时使用,但你只是想使用可用的string表示。

这真的取决于你是否知道o是一个string,你想要做什么。 如果你的评论意味着o真的是一个string,我宁愿直接(string)o投 – 这不可能失败。

使用直接转换的最大优点是,当它失败时,会得到一个InvalidCastException ,它告诉你几乎出了什么问题。

使用as运算符,如果o不是string,则s被设置为null ,如果您不确定并且想要testings ,那么这很方便:

 string s = o as string; if ( s == null ) { // well that's not good! gotoPlanB(); } 

但是,如果您不执行该testing,则稍后将使用s并引发NullReferenceException 。 这些情况往往比较普遍,一旦发生在野外,很难追查到,因为几乎每一行都会引用一个variables,并可能抛出一个variables。 另一方面,如果你想要转换成一个值types(任何原语,或者像DateTime这样的结构体),你必须使用直接转换 – 这as行不通的。

在转换为string的特殊情况下,每个对象都有一个ToString ,所以如果你的第三个方法不为null,那么你的第三个方法可能会好起来,你认为ToString方法可以做你想做的事情。

如果您已经知道可以投射到哪种types,请使用C风格演员:

 var o = (string) iKnowThisIsAString; 

请注意,只有使用C风格转换才能执行显式强制转换。

如果您不知道它是否是您想要的types,并且您要使用它,请使用as关键字:

 var s = o as string; if (s != null) return s.Replace("_","-"); //or for early return: if (s==null) return; 

请注意, 因为不会调用任何types转换运算符。 只有当对象不是null并且是指定types的本地时,它才会是非空的。

使用ToString()来获取任何对象的可读的string表示,即使它不能转换为string。

当你使用FindControl方法时,as关键字在asp.net中是很好的。

 Hyperlink link = this.FindControl("linkid") as Hyperlink; if (link != null) { ... } 

这意味着你可以在types化的variables上进行操作,而不必像直接使用cast那样从object

 object linkObj = this.FindControl("linkid"); if (link != null) { Hyperlink link = (Hyperlink)linkObj; } 

这不是一个巨大的事情,但它节省了代码行和variables赋值,再加上它更具可读性

'as'基于'is',它是一个关键字,它在运行时检查对象是否是polimorphycally兼容的(基本上是可以进行强制转换的),如果检查失败则返回null。

这两个是等价的:

使用“as”:

 string s = o as string; 

使用'是':

 if(o is string) s = o; else s = null; 

相反,C风格转换也是在运行时进行的,但是如果不能进行转换,则会抛出exception。

只是添加一个重要的事实:

“as”关键字仅适用于引用types。 你不能这样做:

 // I swear i is an int int number = i as int; 

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

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

假设a是动物:

 b = a as Badger; c = a as Cow; if (b != null) b.EatSnails(); else if (c != null) c.EatGrass(); 

会得到一个最less的演员喂食。

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

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

“o.ToString()”并不是任何types的转换,它是一个由对象实现的方法,因此,.NET中的每个类都以某种方式实现了“实现”它被调用的类,并返回一个string。

不要忘记,要转换为string,还有Convert.ToString(someType instanceOfThatType)其中someType是一组types之一,本质上是框架的基本types。

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

(这个页面有时会出现一些“非法引用者”的错误,所以只是刷新一下)

结论是,“as”操作符通常比cast更快。 有时候快很多倍,有时候快一点。

我peronsonally事情“为”也更可读。

所以,既然它既快又“安全”(不会抛出exception),而且可能更易于阅读,所以我推荐一直使用“as”。

所有给定的答案是好的,如果我可以添加一些东西:直接使用string的方法和属性(如ToLower),你不能写:

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

你只能写:

 ((string)o).ToLower(); 

但你可以写:

 (o as string).ToLower(); 

as选项更可读(至less对我来说)。

 string s = o as string; // 2 

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

看起来他们两个在概念上是不同的。

直接铸造

types不必严格相关。 它有各种口味。

  • 自定义隐式/显式转换:通常会创build一个新的对象。
  • 值types隐含:复制而不丢失信息。
  • 值types显式:复制和信息可能会丢失。
  • IS-A关系:更改引用types,否则引发exception。
  • 同一types: “铸造是多余的”。

感觉就像对象将被转换成别的东西。

AS运营商

types有直接的关系。 如:

  • 引用types: IS-A关系对象总是相同的,只是引用的变化。
  • 值types: 复制装箱和可空types。

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

样品和IL

  class TypeA { public int value; } class TypeB { public int number; public static explicit operator TypeB(TypeA v) { return new TypeB() { number = v.value }; } } class TypeC : TypeB { } interface IFoo { } class TypeD : TypeA, IFoo { } void Run() { TypeA customTypeA = new TypeD() { value = 10 }; long longValue = long.MaxValue; int intValue = int.MaxValue; // Casting TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL: call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA) IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass ConsoleApp1.Program/IFoo int loseValue = (int)longValue; // explicit -- IL: conv.i4 long dontLose = intValue; // implict -- IL: conv.i8 // AS int? wraps = intValue as int?; // nullable wrapper -- IL: call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0) object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32 TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo //TypeC d = customTypeA as TypeC; // wouldn't compile } 

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

 String s = String.Concat(o); 

由于没有人提到它,最接近实例的关键字Java是这样的:

 obj.GetType().IsInstanceOfType(otherObj) 

使用直接转换string s = (string) o; 如果在你的应用程序的逻辑上下文中是唯一有效的types。 采用这种方法,您将得到InvalidCastException并实现Fail-fast的原则。 您的逻辑将被保护以防止进一步传递无效types,或者如果as运算符使用as则获得NullReferenceException。

如果逻辑需要几个不同的types将string s = o as string; 并检查它为null或使用is运营商。

在C#7.0中出现了新的酷炫function,以简化Cast和Check是模式匹配 :

 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; }