在开发博客、在线代码示例和(最近)甚至是一本书中,我经常遇到这样的代码:

var y = x as T;
y.SomeMethod();

或者,更糟糕的是:

(x as T).SomeMethod();

这对我来说没有意义。如果你确定x是类型T,你应该使用直接强制转换:(T)x。如果不确定,可以使用as,但在执行某些操作之前需要检查是否为空。上面的代码所做的就是将一个(有用的)InvalidCastException转换为一个(没用的)NullReferenceException。

难道只有我一个人认为这是对as关键字的公然滥用吗?还是我错过了一些明显的东西,而上面的模式实际上是有意义的?


当前回答

我认为as关键字可以看作是样式更优雅的版本 dynamic_cast来自c++。

其他回答

Your understanding is true. That sounds like trying to micro-optimize to me. You should use a normal cast when you are sure of the type. Besides generating a more sensible exception, it also fails fast. If you're wrong about your assumption about the type, your program will fail immediately and you'll be able to see the cause of failure immediately rather than waiting for a NullReferenceException or ArgumentNullException or even a logical error sometime in the future. In general, an as expression that's not followed by a null check somewhere is a code smell.

另一方面,如果您对强制转换不确定,并期望它失败,则应该使用as,而不是用try-catch块包装的普通强制转换。此外,建议在类型检查和强制转换之后使用as。而不是:

if (x is SomeType)
   ((SomeType)x).SomeMethod();

它为is关键字生成isinst指令,为强制转换生成castclass指令(有效地执行两次强制转换),你应该使用:

var v = x as SomeType;
if (v != null)
    v.SomeMethod();

这只会生成一条isinst指令。前一种方法在多线程应用程序中有一个潜在的缺陷,因为竞态条件可能会导致变量在is检查成功后改变其类型,并在转换行处失败。后一种方法不容易出现这种错误。


不建议在生产代码中使用以下解决方案。如果你真的讨厌c#中的这种基本构造,你可以考虑切换到VB或其他语言。

如果一个人非常讨厌强制转换语法,他/她可以写一个扩展方法来模仿强制转换:

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

使用整齐的[?语法:

obj.To<SomeType>().SomeMethod()

恕我直言,当与空检查结合在一起时是有意义的:

var y = x as T;
if (y != null)
    y.SomeMethod();

直接强制转换比as关键字更需要一对括号。所以即使在你100%确定是什么类型的情况下,它也减少了视觉上的混乱。

不过我同意例外的情况。但至少对我来说,大多数as的使用都是在之后检查null,我发现这比捕获异常更好。

我在这里写了一点:

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

我明白你的意思。我同意它的重点:强制转换操作符传达的是“我确信这个对象可以转换为那种类型,如果我错了,我愿意冒一个异常的风险”,而“as”操作符传达的是“我不确定这个对象可以转换为那种类型;如果我错了,就给我一个null。”

然而,有一个微妙的区别。whatever()表示“我知道x不仅可以转换为T,而且这样做只涉及引用或开箱转换,而且x不是空的”。这确实传达了不同于(T)x). whatever()的信息,也许这就是代码作者的意图。

99%的情况下,当我使用“as”时,我不确定实际的对象类型是什么

var x = obj as T;
if(x != null){
 //x was type T!
}

我不想捕捉显式的强制转换异常,也不想使用"is"进行两次强制转换:

//I don't like this
if(obj is T){
  var x = (T)obj; 
}