直接铸造与“作为”操作员?

考虑以下代码:

void Handler(object o, EventArgs e){// I swear o is a stringstring s = (string)o; // 1//-OR-string s = o as string; // 2// -OR-string s = o.ToString(); // 3}

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

198805 次浏览

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

这两个是等价的:

使用'as':

string s = o as string;

使用'is':

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

相反,c样式转换也是在运行时进行的,但如果无法进行转换,则会引发异常。

只是补充一个重要的事实:

'as'关键字仅适用于引用类型。你不能这样做:

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

在这种情况下,您必须使用铸造。

string s = (string)o; // 1

如果o不是string,则抛出错误码InvalidCastException。否则,将o分配给s,即使onull

string s = o as string; // 2

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

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

如果onull,则导致空引用异常。将o.ToString()返回的任何内容分配给s,无论o是什么类型。


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

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

这真的取决于你是否知道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并抛出空引用异常。这些往往更常见,并且一旦在野外发生就更难追踪很多,因为几乎每一行都取消引用一个变量并可能抛出一个。另一方面,如果您尝试转换为值类型(任何基元或结构,例如时间戳),您必须使用直接转换-as将无法工作。

在转换为字符串的特殊情况下,每个对象都有一个ToString,所以如果o不为空,并且您认为ToString方法可能会做您想要的事情,那么您的第三个方法可能没问题。

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

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

“o. ToString()”本身不是任何类型的强制转换,它是由对象实现的方法,因此以某种方式由. net中的每个类实现,这些类对它所调用的类的实例“做某事”并返回一个字符串。

不要忘记,为了转换为字符串,还有Conversion. ToString(一些类型instance OfThatType),其中一些类型是一组类型之一,本质上是框架的基本类型。

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

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

假设一个是一个动物:

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

将以最少的投射获得一个

如果您已经知道它可以转换为什么类型,请使用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;

请注意,不会调用任何类型转换运算符。只有当对象不为空且本机指定类型时,它才会非空。

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

string s = o as string; // 2

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

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

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

这意味着您可以对类型化变量进行操作,而不必像直接转换一样从object转换它:

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

这不是一个巨大的东西,但它节省了代码行和变量赋值,并且更具可读性

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

(此页面有时会出现一些“非法引用者”错误,因此如果出现,请刷新)

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

我个人认为“作为”也更具可读性。

因此,由于它既更快又“更安全”(不会抛出异常),并且可能更易于阅读,我建议始终使用“as”。

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

String s = String.Concat(o);

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

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

你只能写:

((string)o).ToLower();

但你可以这样写:

(o as string).ToLower();

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

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

obj.GetType().IsInstanceOfType(otherObj)

似乎两者在概念上是不同的。

直接铸造

类型不一定要严格相关。它有各种口味。

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

感觉这个物体要被转换成别的东西。

as操作员

类型有直接的关系。如:

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

感觉就像你要用不同的方式处理这个物体。

样品和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;
// CastingTypeB 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.i4long dontLose = intValue; // implict -- IL: conv.i8
// ASint? 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.Int32TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeDIFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo
//TypeC d = customTypeA as TypeC; // wouldn't compile}

如果在应用程序的逻辑上下文中string是唯一有效的类型,请使用直接转换string s = (string) o;。使用这种方法,你将获得InvalidCastException并实现快速失败的原则。如果使用as运算符,你的逻辑将受到保护,不会进一步传递无效类型或获得NullReReference ceException。

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

C#7.0中出现了新的酷炫功能,以简化转换并检查是否为模式匹配

if(o is string s){// Use string variable s}
or
switch (o){case int i:// Use int variable ibreak;case string s:// Use string variable sbreak;}

我想提请注意操作员的以下细节:

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

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