为什么要在使用“as”时转换引用类型?

时间:2010-08-13 09:53:55

标签: c# casting

  

可能重复:
  Casting: (NewType) vs. Object as NewType

在C#中,为什么要在使用“as”时转换引用类型?

投射可以生成例外,而如果投射失败,“as”将评估为null

在所有情况下,“as as”不会更容易使用引用类型吗?

例如:

MyObject as DataGridView

而不是

(DataGridView)MyObject

8 个答案:

答案 0 :(得分:9)

考虑以下备选方案:

Foo(someObj as SomeClass);

Foo((SomeClass)someObj);

由于someObj类型错误,第一个版本会将null传递给Foo。一段时间后,这会导致NullReferenceException被抛出。多久以后?取决于Foo的作用。它可能会将null存储在一个字段中,然后几分钟后,某些代码会访问该字段,而不是null

但是对于第二个版本,您会立即发现问题。

为什么要修复错误更难?

<强>更新

OP在评论中提到:不是更容易使用as然后在null声明中检查if吗?

如果null出乎意料并且是来电者错误的证据,您可以说:

SomeClass c = someObj as SomeClass;
if (c == null)
{
    // hmm...
}

你在if - 块中做了什么?有两种通用解决方案。一种是抛出异常,因此调用者有责任处理他们的错误。在这种情况下,写起来肯定更简单:

SomeClass c = (SomeClass)someObj;

它可以简单地节省您手动编写if / throw逻辑。

但还有另一种选择。如果你有SomeClass的“库存”实现,你很乐意使用哪里没有更好的可用(也许它的方法什么都不做,或返回“空”值等),那么你可以这样做:

SomeClass c = (someObj as SomeClass) ?? _stockImpl;

这将确保c永远不会null。但这真的更好吗?如果来电者有错误怎么办?你不想帮助发现bug吗?通过交换默认对象,您可以伪装该错误。这听起来像是一个有吸引力的想法,直到你浪费一周的时间来追踪一个错误。

(在某种程度上,这模仿了Objective-C的行为,其中任何使用null引用的尝试都将永远不会抛出;它只是默默无效。)

答案 1 :(得分:2)

operator'as'仅适用于引用类型。

答案 2 :(得分:2)

有时,您希望抛出异常。有时候,你想尝试转换和null是可以的。如前所述,as不适用于值类型。

答案 3 :(得分:1)

如果在编写代码以进行演员表时,您确定演员表应该有效,您应该使用(DataGridView)MyObject。这样,如果转换在将来失败,那么关于MyObject类型的假设将在您进行转换时产生无效的转换异常,而不是在稍后的某个时刻引起空引用异常。

如果你确实想要处理MyObject不是DataGridView的情况,那么使用as,并且可能在使用它之前检查它是否为null。

tl; dr 如果您的代码假设某些内容,并且该假设在运行时出错,则代码应该抛出异常。

答案 4 :(得分:1)

一个明确的原因是,对象是或者可能(在编写通用方法时,您可能不知道在编码时)被强制转换为值类型,在这种情况下as是不允许的

另一个可疑的原因是你已经知道对象属于有问题的类型。只是多么可疑取决于你如何知道这一点。在以下情况中:

if(obj is MyType)
  DoMyTypeStuff((MyType)obj);
else
  DoMoreGeneralStuff(obj);

这里很难证明使用as是正确的,因为它唯一真正做的就是添加冗余检查(可能它会被优化掉,也许它不会)。在另一个极端,如果你在恍惚状态的一半,你已经掌握了大量的信息,那么你就可以确定该对象必须属于输入有问题,也许最好添加一下支票。

另一个很好的理由是,as隐藏了错误类型和null之间的区别。如果将字符串传递给给定方法(包括空字符串)是合理的,但是传递int是不合理的,那么val as string刚刚使用不正确的用法看起来像一个完全不同的正确用法,并且你刚刚让臭虫更难找到并且可能更具破坏性。

最后,也许如果您不知道对象的类型,则调用代码应该。如果调用代码错误地调用了您的调用代码,则应该收到异常。要么允许InvalidCastException传回,要么捕获它并抛出InvalidArgument异常或类似事件,这是一种合理而明确的方法。

答案 5 :(得分:0)

来自MSDN(as (C# reference)):

  

as运算符仅执行引用转换和装箱转换。 as运算符不能执行其他转换,例如用户定义的转换,而应使用强制转换表达式执行转换。

答案 6 :(得分:0)

考虑到所有评论,我们在前几天遇到了这个问题,并想知道为什么要使用关键字as进行直接投射。如果您想要演员表失败怎么办?如果您从空对象进行转换,这有时是您希望从转换中获得的理想效果。然后将异常推送到调用堆栈。

因此,如果您想要失败,请使用直接投射,如果您没有失败,请使用as关键字。

答案 7 :(得分:0)

更快,不会抛出异常。因此通常是优选的。使用演员表的原因包括:

使用as,您只能将继承树中较低的类型分配给较高的类型。例如:

object o = "abc" as object;
DataGridView d = "abc" as DataGridView // doesn't do anything

DataGridView可以创建允许此操作的自定义转换。转换是在目标类型上定义的,因此允许所有内容,只要它已定义。

另一个问题是它并不总是有效。考虑这种方法:

IEnumerable<T> GetList<T>(T item)
{
  (from ... select) as IEnumerable<T>
}

此代码失败,因为T也可能是值类型。你不能使用那些因为它们永远不会为空。这意味着你必须对T施加约束,而实际上它是不必要的。如果您不知道自己是否会有引用类型,则永远不能使用as。

当然,使用as关键字时应始终检查null。不要假设不会抛出任何异常,因为关键字不会抛出任何异常。不要在它周围放一个Try {} Catch(NullReferenceException){},这是不必要的和臃肿的。只需将值赋给变量并在使用之前检查null。切勿在方法调用中使用内联。