演员与'as'运营商重访

Tor*_*gen 21 c# clr

我知道有几个帖子已经涉及演员和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')
}
Run Code Online (Sandbox Code Playgroud)

请不要介意这个明显懊悔的例子是否是好的做法.我在这里关注的是两者之间非常有趣的差异,因为演员阵容不会编译,而不是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
}
Run Code Online (Sandbox Code Playgroud)

为什么as运算符编译和转换不?

Eri*_*ert 31

解决您的第一个问题:as运营商不仅忽视用户定义的转换,尽管这是相关的.更重要的是,演员操作员做了两件相互矛盾的事情.演员经营者意味着:

  1. 我知道编译时类型Foo的这个表达式实际上是运行时类型Bar的对象.编译器,我现在告诉你这个事实,以便你可以利用它.假设我是正确的,请生成代码; 如果我不正确,那么你可以在运行时抛出异常.

  2. 我知道编译时类型Foo的这个表达式实际上是运行时类型Foo.有一种将Foo的部分或全部实例转换为Bar实例的标准方法.编译器,请生成这样的转换,如果在运行时发现转换的值不可转换,则在运行时抛出异常.

那些是对立的.巧妙的技巧,让一个运营商做相反的事情.

as相比之下,操作员只具有第一感.一个as只做装箱,拆箱,并表示保留转换.演员可以完成所有这些以及更改表示更改的转换.例如,将int转换为short会将表示形式从四字节整数更改为两字节整数.

这就是为什么"原始"演员在无约束的仿制品上不合法的原因; 因为编译器没有足够的信息来确定它是什么类型的转换:装箱,拆箱,表示保留或表示改变.用户的期望是通用代码中的强制转换具有更强类型代码中的强制转换的所有语义,并且我们无法有效地生成该代码.

考虑:

void M<T, U>(T t, out U u)
{
    u = (U)t;
}
Run Code Online (Sandbox Code Playgroud)

你希望它能起作用吗?我们生成哪些代码可以处理:

M<object, string>(...); // explicit reference conversion
M<string, object>(...); // implicit reference conversion
M<int, short>(...); // explicit numeric conversion
M<short, int>(...); // implicit numeric conversion
M<int, object>(...); // boxing conversion
M<object, int>(...); // unboxing conversion
M<decimal?, int?>(...); // lifted conversion calling runtime helper method
// and so on; I could give you literally hundreds of different cases.
Run Code Online (Sandbox Code Playgroud)

基本上我们必须为再次启动编译器的测试发出代码,对表达式进行全面分析,然后发出新代码.我们在C#4中实现了这个功能; 它被称为"动态",如果这是你想要的行为,你可以随意使用它.

我们没有这些问题as,因为as只有三件事.它可以进行装箱转换,拆箱转换和类型测试,我们可以轻松生成执行这三项操作的代码.


Ran*_*832 5

我们是否应该怀疑演员阵容在编译时是全部还是部分解决而且作为运算符不是?

你自己给出了答案,在你的问题开始:"as运算符不使用用户定义的转换操作符" -同时,中投确实,这意味着它需要找到那些经营者(或他们的缺席在编译时).

请注意,就编译器而言,类型T和Bar之间没有已知的连接(编译时未知).

的T型的事实未知意味着编译器无法知道是否有它和酒吧之间没有任何联系.

请注意,这(Bar)(object)foo确实有效,因为没有类型可以将转换运算符赋予Object [因为它是所有内容的基类],并且已知从对象到Bar的转换不必处理转换运算符.