【发布时间】:2011-05-23 15:48:10
【问题描述】:
我知道已经有几篇关于演员表和as 运算符之间区别的帖子。他们大多都重申了相同的事实:
-
as运算符不会抛出异常,但如果转换失败则返回null - 因此,
as运算符仅适用于引用类型 -
as运算符不会使用用户定义的转换运算符
然后,答案往往会无休止地争论如何使用或不使用其中一种,以及每种方法的优缺点,甚至它们的性能(我根本不感兴趣)。
但这里还有更多工作要做。考虑:
static void MyGenericMethod<T>(T foo)
{
var myBar1 = foo as Bar; // compiles
var myBar2 = (Bar)foo; // does not compile ('Cannot cast expression of
// type 'T' to type 'Bar')
}
请不要介意这个明显悔恨的例子是否是好的做法。我在这里担心的是两者之间非常有趣的差异,因为演员不会编译,而as 可以。我真的很想知道是否有人可以对此有所了解。
正如经常提到的,as 运算符忽略了用户定义的转换,但在上面的示例中,它显然是两者中更强大的。请注意,as 就编译器而言,(编译时未知)类型 T 和 Bar 之间没有已知的联系。演员阵容完全是“运行时”的。我们是否应该怀疑强制转换在编译时已全部或部分解决,而 as 运算符则没有?
顺便说一句,添加类型约束不出所料地修复了强制转换,因此:
static void MyGenericMethod<T>(T foo) where T : Bar
{
var myBar1 = foo as Bar; // compiles
var myBar2 = (Bar)foo; // now also compiles
}
为什么as 操作符可以编译而强制转换不行?
【问题讨论】:
-
在我看来
foo as Bar是指定类型参数,而(Bar)foo正在尝试对未指定类型T进行强制转换。这就是第二个示例有效的原因。您已将类型参数限制为可转换类型。第一个示例无法编译,因为不能保证foo可以类型安全地转换为Bar。 -
为什么 as 操作符可以编译而强制转换不行?
-
@Robert:我不明白你在说什么。那应该抛出一个
InvalidCastException,或者as也不应该编译。 -
@robert:所以你是说 cast 运算符需要这样的保证,而 as 运算符不需要?为什么?只是编译器团队的任意设计决定?
-
如果您指定类型
T作为要转换的东西,编译时无法保证您在运行时指定的实际类型将转换为Bar。泛型是编译时构造;类型安全检查在编译时完成。由于无法在编译时检查(Bar)foo的类型安全性,因此会发生编译错误。但是使用as运算符指定类型;它将运行时类型限制为Bar。所以不会出现编译错误,因为编译器可以推断出Bar是可转换的。