考虑以下代码:
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
}
三种类型的施法有什么区别(好吧,第三种不是施法,但你明白了意图)。应该首选哪一种?
考虑以下代码:
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
}
三种类型的施法有什么区别(好吧,第三种不是施法,但你明白了意图)。应该首选哪一种?
当前回答
string s = o as string; // 2
首选,因为它避免了双重铸造的性能损失。
其他回答
看起来他们两个在概念上是不同的。
直接铸造
类型不必严格相关。它有各种口味。
自定义隐式/显式转换:通常会创建一个新对象。值类型隐式:复制而不丢失信息。值类型显式:副本和信息可能会丢失。IS-A关系:更改引用类型,否则引发异常。相同类型:“铸造是多余的”。
感觉对象将被转换为其他对象。
AS操作员
类型有直接关系。如:
引用类型:IS-A关系对象始终相同,只是引用更改。值类型:复制装箱和可为null的类型。
感觉就像你将以不同的方式处理对象。
样品和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
}
string s = o as string; // 2
首选,因为它避免了双重铸造的性能损失。
如果您已经知道它可以转换为什么类型,请使用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不会调用任何类型转换运算符。仅当对象不为空且本机为指定类型时,它才为非空。
使用ToString()获取任何对象的可读字符串表示,即使它不能转换为字符串。
根据本页上运行的实验:http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(此页面有时会显示一些“非法引用者”错误,因此如果出现,请刷新)
结论是,“as”运算符通常比强制转换快。有时速度快很多倍,有时仅仅快不了几倍。
我个人认为“as”也更可读。
因此,由于它既快又“安全”(不会抛出异常),而且可能更容易阅读,我建议一直使用“as”。
string s = (string)o; // 1
如果o不是字符串,则引发InvalidCastException。否则,将o赋给s,即使o为空。
string s = o as string; // 2
如果o不是字符串或o为空,则将null赋给s。因此,不能将其用于值类型(在这种情况下,运算符永远不会返回null)。否则,将o指定给s。
string s = o.ToString(); // 3
如果o为空,则导致NullReferenceException。无论o是什么类型,都将任何o赋值给ToString()返回的值。
大多数转换都使用1-它简单明了。我倾向于几乎从不使用2,因为如果某个东西不是正确的类型,我通常会发生异常。我只看到这种返回null类型的功能需要设计糟糕的库,这些库使用错误代码(例如,返回null=error,而不是使用异常)。
3不是强制转换,只是方法调用。当您需要非字符串对象的字符串表示时,请使用它。