Direct casting vs 'as' operator?
请考虑以下代码:
1 2 3 4 5 6 7 8 9 | 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 } |
这三种类型的角色扮演有什么区别(好吧,第三种不是角色扮演,但是你有这个意图)。应该首选哪一个?
1 | string s = (string)o; // 1 |
如果
1 | string s = o as string; // 2 |
如果
1 | string s = o.ToString(); // 3 |
如果
对于大多数转换,使用1-这是简单和简单的。我几乎从不使用2,因为如果某个类型不正确,我通常希望发生异常。我只看到了对这种返回空类型的功能的需要,这些功能使用错误代码(例如,返回空=错误,而不是使用异常)。
3不是强制转换,只是一个方法调用。用于需要非字符串对象的字符串表示形式时。
这取决于你是否知道
使用直接强制转换的最大好处是,当它失败时,您会得到一个InvalidcastException,它几乎告诉您出了什么问题。
使用
1 2 3 4 5 6 | string s = o as string; if ( s == null ) { // well that's not good! gotoPlanB(); } |
但是,如果不执行该测试,稍后将使用
在转换为字符串的特殊情况下,每个对象都有一个
如果您已经知道它可以转换为什么类型,请使用C样式转换:
1 | var o = (string) iKnowThisIsAString; |
注意,只有使用C样式转换才能执行显式类型强制。
如果您不知道它是否是所需的类型,并且要使用它(如果是),请使用as关键字:
1 2 3 4 5 | var s = o as string; if (s != null) return s.Replace("_","-"); //or for early return: if (s==null) return; |
注意,as不会调用任何类型转换运算符。只有当对象不是空的并且是指定类型的本机对象时,它才是非空的。
使用toString()获取任何对象的可读字符串表示形式,即使它不能转换为字符串。
使用findcontrol方法时,as关键字在ASP.NET中很好。
1 2 3 4 5 | Hyperlink link = this.FindControl("linkid") as Hyperlink; if (link != null) { ... } |
这意味着您可以对类型化变量进行操作,而不是像使用直接强制转换那样从
1 2 3 4 5 | object linkObj = this.FindControl("linkid"); if (link != null) { Hyperlink link = (Hyperlink)linkObj; } |
这不是一件大事,但它节省了代码行和变量分配,而且更具可读性
"as"基于"is",这是一个关键字,用于在运行时检查对象是否与策略兼容(基本上,如果可以进行强制转换),如果检查失败,则返回空值。
这两个相当:
使用"AS":
1 | string s = o as string; |
使用"IS":
1 2 3 4 |
相反,C样式的强制转换也在运行时进行,但如果无法进行强制转换,则抛出异常。
只是为了增加一个重要事实:
"as"关键字只能用于引用类型。你做不到:
1 2 | // I swear i is an int int number = i as int; |
在这种情况下,你必须使用铸造。
2对于强制转换为派生类型很有用。
假设A是一种动物:
1 2 3 4 5 6 7 | b = a as Badger; c = a as Cow; if (b != null) b.EatSnails(); else if (c != null) c.EatGrass(); |
将得到一个有最少石膏的饲料。
"(string)o"将导致无效的强制转换,因为没有直接强制转换。
"o a s string"将导致s为空引用,而不是引发异常。
"o.toString()"本身不是任何类型的强制转换,它是由对象实现的方法,因此在某种程度上是由.NET中的每个类实现的,这些类对调用它的类的实例"做些什么",并返回一个字符串。
别忘了,对于转换为字符串,还有convert.toString(someType instanceOfthatType),其中someType是一组类型中的一种,本质上是框架基类型。
根据本页的实验结果:http://www.dotnettguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(此页面有时会出现一些"非法引用"错误,因此如果出现错误,只需刷新即可)
结论是,"as"操作符通常比cast更快。有时快了很多倍,有时快得不到多少。
我个人认为"as"也更易读。
因此,由于它既快又"安全"(不会抛出异常),而且可能更容易阅读,所以我建议始终使用"as"。
所有给出的答案都是好的,如果我可以加上一些:要直接使用字符串的方法和属性(例如ToLower),您不能编写:
1 | (string)o.ToLower(); // won't compile |
您只能写:
1 | ((string)o).ToLower(); |
但是你可以写:
1 | (o as string).ToLower(); |
他们两个在概念上似乎是不同的。
直接铸造
类型不必严格相关。它有各种口味。
- 自定义隐式/显式转换:通常创建一个新对象。
- 值类型隐式:复制而不丢失信息。
- 值类型显式:复制和信息可能丢失。
- IS-A关系:更改引用类型,否则引发异常。
- 同一类型:"铸造是多余的"。
感觉这个物体会被转换成别的东西。
AS算子
类型具有直接关系。如:
- 引用类型:IS-A关系对象总是相同的,只是引用发生了变化。
- 值类型:复制装箱和可以为空的类型。
感觉就像你要用不同的方式处理物体。
样品与IL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 } |
1 | string s = o as string; // 2 |
优先考虑,因为它避免了双铸的性能损失。
我想提醒大家注意AS运营商的以下细节:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as
Note that the as operator performs only reference conversions,
nullable conversions, and boxing conversions. The as operator can't
perform other conversions, such as user-defined conversions, which
should instead be performed by using cast expressions.
当试图获取可能为空的任何(任何类型)的字符串表示形式时,我更喜欢下面的代码行。它很紧凑,它调用toString(),并且正确地处理空值。如果o为空,s将包含string.empty。
1 | String s = String.Concat(o); |
由于没有人提到它,最接近Java的实例的关键字是:
1 | obj.GetType().IsInstanceOfType(otherObj) |
如果在应用程序的逻辑上下文中,
如果逻辑需要几个不同类型的强制转换
新的酷特性出现在C 7.0中以简化转换,检查是模式匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } |
C支持以下两种类型转换(强制转换):
γ
(c)v
?在给定表达式中将V的静态类型转换为C
?仅当V的动态类型为C或C的子类型时才可能
?如果不是,则抛出InvalidcastException
γ
V为C
?(c)v的非致命变种
?因此,将给定表达式中的静态类型v转换为c
?如果V的动态类型不是C或C的子类型,则返回空值