再谈演员与“作为”操作员 [英] Cast vs 'as' operator revisited

查看:68
本文介绍了再谈演员与“作为”操作员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道已经有几篇关于强制转换和 as 运算符之间差异的文章。他们大多都重申了相同的事实:

I know there are several posts already concerning the difference between casts and the as operator. They all mostly restate the same facts:


  • as 运算符不会抛出错误,但是如果强制转换失败则返回 null

  • 因此,仅 as 运算符使用引用类型

  • as 运算符将不使用用户定义的转换运算符

  • The as operator will not throw, but return null if the cast fails
  • Consequently, the as operator only works with reference types
  • The as operator will not use user-defined conversion operators

然后,回答者会无休止地争论如何使用一个或另一个以及它们各自的优缺点,甚至它们的性能(我一点都不感兴趣)。

Answers then tend to debate endlessly the how to use or not use the one or the other and the pros and cons of each, even their performance (which interests me not at all).

但是这里还有更多工作要做。考虑:

But there is something more at work here. Consider:

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')
}

请不要介意这个明显痛悔的例子是否是好的做法。我在这里担心的是两者之间非常有趣的差异,因为强制转换将不会编译,而就像一样。我真的很想知道是否有人可以对此有所了解。

Please never mind whether this obviously contrite example is good practice or not. My concern here is the very interesting disparity between the two in that the cast will not compile whereas the as does. I really would like to know if anyone could shed some light on this.

通常会注意到, as 运算符不考虑用户定义的转换,但是在上面的示例中,显然这两者具有更强的功能。注意,就编译器而言, 在(编译时未知)类型T和Bar之间没有已知连接。演员表完全是运行时。我们是否应该怀疑是否在编译时全部或部分地解决了转换问题,而 as 运算符却没有解决?

As is often noted, the as operator disregards user-defined conversions, but in the above example, it is clearly the more capable of the two. Note that as far as the compiler is concerned, there is no known connection between the (unknown at compile-time) type T and Bar. The cast is entirely 'run-time'. Should we suspect that the cast is resolved, wholly or partly, at compile time and the as operator not?

顺便说一句,添加类型约束无疑会修复强制转换,因此:

By the way, adding a type constraint unsurprisingly fixes the cast, thus:

static void MyGenericMethod<T>(T foo) where T : Bar
{
    var myBar1 = foo as Bar;  // compiles
    var myBar2 = (Bar)foo;    // now also compiles
}

为什么 as 运算符可以编译,强制转换可以吗?

Why does the as operator compile and the cast not?

推荐答案

要解决您的第一个问题:不仅仅是 as 运算符会忽略用户定义的转换,尽管这很重要。更相关的是,强制转换运算符执行两个矛盾的操作。强制转换运算符的意思是:

To address your first question: it is not just that the as operator disregards user-defined conversions, though that is relevant. What is more relevant is that the cast operator does two contradictory things. The cast operator means either:


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

  1. I know that this expression of compile-time type Foo will actually be an object of runtime-type Bar. Compiler, I am telling you this fact now so that you can make use of it. Please generate code assuming that I am correct; if I am incorrect, then you may throw an exception at runtime.

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

I know that this expression of compile-time type Foo will actually be of runtime type Foo. There is a standard way of converting some or all instances of Foo to an instance of Bar. Compiler, please generate such a conversion, and if it turns out at runtime that the value being converted is not convertible, then throw an exception at runtime.

那些是相反的

相比之下, as 运算符仅具有第一层含义。 。 as 仅执行装箱取消装箱表示保留转换。强制转换可以完成所有这些操作,还可以进行其他更改表示的转换。例如,将int强制转换为short会将表示形式从四字节整数更改为两字节整数。

The as operator by contrast only has the first sense. An as only does boxing, unboxing and representation-preserving conversions. A cast can do all of those plus additional representation-changing conversions. For example, casting int to short changes the representation from a four-byte integer to a two-byte integer.

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

That's why "raw" casts are not legal on unconstrained generics; because the compiler does not have enough information to figure out what kind of cast it is: boxing, unboxing, representation-preserving or representation-changing. The expectation of users is that a cast in generic code has all the semantics of a cast in more strongly typed code, and we have no way to generate that code efficiently.

考虑:

void M<T, U>(T t, out U u)
{
    u = (U)t;
}

您希望这样做吗?我们生成的代码可以处理:

Do you expect that to work? What code do we generate that can handle:

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.

基本上,我们必须为重新启动编译器的测试发出代码,对表达式进行了全面分析,然后发出了新代码。我们在C#4中实现了该功能。这就是所谓的动态,如果这是您想要的行为,则可以随时使用它。

Basically we would have to emit code for the test that started the compiler again, did a full analysis of the expressions, and then emitted new code. We implemented that feature in C# 4; it's called "dynamic" and if that's the behaviour you want, you can feel free to use it.

对于,我们没有这些问题,因为 as 只做三件事。它可以进行装箱转换,取消装箱转换和类型测试,并且我们可以轻松生成执行这三件事的代码。

We have none of these problems with as, because as only does three things. It does boxing conversions, unboxing conversions, and type tests, and we can easily generate code that does those three things.

这篇关于再谈演员与“作为”操作员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆