C # “ as”演员和经典演员

可能的复制品:
使用 CLR 中的“ as”关键字进行转换 vs

我最近学到了一种不同的施法方法

SomeClass someObject = (SomeClass) obj;

人们可以使用这样的语法:

SomeClass someObject = obj as SomeClass;

如果 obj 不是 SomClass,它似乎返回 null,而不是抛出类强制转换异常。

我发现,如果强制转换失败并尝试访问 some Object 变量,这可能导致 NullReferenceException。所以我想知道这个方法背后的原理是什么?为什么应该使用这种方式而不是(旧的)方式进行强制转换呢? 这似乎只是将失败的强制转换问题“深入”到代码中。

128045 次浏览

使用“经典”方法,如果强制转换失败,将抛出 InvalidCastException。使用 as方法,它会生成 null,可以对其进行检查,并避免抛出异常。

此外,只能对引用类型使用 as,因此如果要对值类型进行类型转换,则仍必须使用“经典”方法。

注:

as方法只能用于可以分配 null值的类型。用于仅表示引用类型,但当。NET 2.0出来了,它引入了可空值类型的概念。由于可以为这些类型分配一个 null值,所以它们对于 as操作符是有效的。

如果不是有效的强制转换,则使用 as 将返回 null,该强制转换允许您执行除了在 try/catch 中包装强制转换之外的其他操作。我讨厌经典的演员阵容。如果我不确定,我总是用石膏。另外,例外情况代价高昂。空检查不是。

空比较比抛出和捕获异常快 很多。异常具有显著的开销-堆栈跟踪必须组装等。

异常应该表示一种意外的状态,这种状态通常不能表示情况(也就是 as工作得更好的时候)。

没什么大不了的。.基本上,测试某些东西是否属于某种类型(比如使用‘ as’)是很方便的。您需要检查“ as”调用的结果,以确定结果是否为 null。

当您希望强制转换工作并希望引发异常时,请使用“经典”方法。

您可以使用“ as”语句来避免出现异常的可能性,例如,您可以通过逻辑优雅地处理强制转换失败。只有在确定对象是所需类型时才使用强制转换。我几乎总是使用“ as”,然后检查 null。

在某些情况下,处理 null比处理异常容易,特别是合并运算符很方便:

SomeClass someObject = (obj as SomeClass) ?? new SomeClass();

它还简化了基于对象类型进行分支的代码(不使用多态性) :

ClassA a;
ClassB b;
if ((a = obj as ClassA) != null)
{
// use a
}
else if ((b = obj as ClassB) != null)
{
// use b
}

根据 MSDN 页面中的指定,as操作符相当于:

expression is type ? (type)expression : (type)null

这完全避免了异常,有利于更快的类型测试,但是也限制了对支持 null(引用类型和 Nullable<T>)的类型的使用。

当你真正的 不知道变量可能是什么类型时,as关键字是有用的。如果您有一个函数,它将根据参数的实际类型遵循不同的代码路径,那么您有两种选择:

首先,使用普通的石膏:

if(myObj is string)
{
string value = (string)myObj;


... do something
}
else if(myObj is MyClass)
{
MyClass = (MyClass)myObj;
}

这要求您使用 is检查对象的类型,这样您就不会尝试将其强制转换为会失败的对象。这也有点多余,因为 is类型的检查是在强制转换中再次完成的(这样它就可以在需要时抛出异常)。

另一种方法是使用 as

string myString = myObj as string;
MyClass myClass = myObj as MyClass;


if(myString != null)
{


}
else if(myClass != null)
{


}

这使得代码稍微短了一些,也消除了冗余的类型检查。

我认为最好的“规则”应该是只使用“ as”关键字,因为这意味着你的主题不会成为你要强加给它的对象:

var x = GiveMeSomething();


var subject = x as String;


if(subject != null)
{
// do what you want with a string
}
else
{
// do what you want with NOT a string
}

然而,当你的主题应该是类型,你正在铸造,使用“经典铸造”,如你所说。因为如果它不是您所期望的类型,那么您将得到一个适合异常情况的异常。

如果强制转换的结果将传递给一个方法,您知道该方法将处理 null 引用,而不会抛出和 ArgumentNullException或类似的东西,那么我认为这是有用的。

我发现 as几乎没有什么用处,因为:

obj as T

慢于:

if (obj is T)
...(T)obj...

as的使用对我来说是一个非常边缘化的场景,所以我想不出任何一般规则来说明什么时候可以使用它,而不仅仅是在堆栈上进一步进行强制转换和处理(更多信息)强制转换异常。

在一些情况下,as操作符是有用的。

  1. 当您只需要知道一个对象是特定类型的,但不需要特别作用于该类型的成员时
  2. 当您希望避免异常而显式处理 null
  3. 您想知道对象之间是否存在 CLR 转换,而不仅仅是某个用户定义的转换。

第三点很微妙,但很重要。在强制转换操作符和 as操作符之间不存在1-1映射。as运算符严格限于 CLR 转换,不会考虑用户定义的转换(强制转换运算符将)。

具体来说,as操作符只允许执行以下操作(来自 C # lang 规范的7.9.11节)

  • 从 E 到 T 的类型存在标识(6.1.1)、隐式引用(6.1.6)、装箱(6.1.7)、显式引用(6.2.4)或取消装箱(6.2.5)转换。
  • E 或 T 的类型是开放类型。
  • E 是空字面值。