直接施放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
,即使o
为null
。
string s = o as string; // 2
如果o
不是一个string
或者o
是null
则将null
赋值给s
。 出于这个原因,你不能使用它的值types(运算符在这种情况下永远不能返回null
)。 否则,将o
赋给s
。
string s = o.ToString(); // 3
如果o
为null
则会导致NullReferenceException 。 分配任何o.ToString()
返回给s
,不pipeo
是什么types。
使用1进行大多数转换 – 这很简单直接。 我倾向于几乎从不使用2,因为如果事情不是正确的types,我通常期望发生exception。 我只看到需要使用错误代码(例如,返回null =错误,而不是使用exception)的糟糕的devise库的这种return-nulltypes的function。
3不是一个强制转换,只是一个方法调用。 在需要非string对象的string表示时使用它。
- 当一些东西肯定是其他的东西时使用。
- 当有事可能是另一回事时使用。
- 当你不关心它是什么时使用,但你只是想使用可用的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; }