为什么转换和转换操作在语法上无法区分?

Why are casting and conversion operations are syntactically indistinguishable?

Stack Overflow 有几个关于转换盒装值的问题:1, 2

解决方案首先需要 unbox 值,然后才将其转换为另一种类型。尽管如此,盒装值 "knows" 是它自己的类型,我看不出为什么不能调用转换运算符。

此外,同样的问题也适用于引用类型:

void Main()
{
    object obj = new A();
    B b = (B)obj;   
}


public class A 
{
}

public class B {}

此代码抛出 InvalidCastException。所以这不是值与引用类型的问题;这就是编译器的行为方式。

对于上面的代码,它发出 castclass B,对于代码

void Main()
{
    A obj = new A();
    B b = (B)obj;   
}

public class A 
{
    public static explicit operator B(A obj)
    {
        return new B();
    }
}

public class B
{
}

它发出 call A.op_Explicit

啊哈!这里编译器看到 A 有一个运算符并调用它。但是如果 B 继承自 A 会发生什么?没那么快,编译器很聪明……它只是说:

A.explicit operator B(A)': user-defined conversions to or from a derived class are not allowed

哈!没有歧义!

但到底为什么他们允许两种截然不同的操作看起来一样?!是什么原因?

转换运算符本质上是 "glorified method calls",因此编译器(相对于运行时)已经需要知道您要使用转换运算符而不是类型转换。基本上,编译器需要检查是否存在转换才能为其生成适当的字节码。

您的第一个代码示例基本上看起来像 "convert from object to B",因为编译器不知道变量只能包含 A。根据规则,这意味着编译器必须发出类型转换操作。

你的第二个代码示例对编译器来说是显而易见的,因为 "convert from A to B" 可以用转换运算符完成。

据我所知,您的观察就是我在这里所做的观察:

http://ericlippert.com/2009/03/03/representation-and-identity/

There are two basic usages of the cast operator in C#:

(1) My code has an expression of type B, but I happen to have more information than the compiler does. I claim to know for certain that at runtime, this object of type B will actually always be of derived type D. I will inform the compiler of this claim by inserting a cast to D on the expression. Since the compiler probably cannot verify my claim, the compiler might ensure its veracity by inserting a run-time check at the point where I make the claim. If my claim turns out to be inaccurate, the CLR will throw an exception.

(2) I have an expression of some type T which I know for certain is not of type U. However, I have a well-known way of associating some or all values of T with an “equivalent” value of U. I will instruct the compiler to generate code that implements this operation by inserting a cast to U. (And if at runtime there turns out to be no equivalent value of U for the particular T I’ve got, again we throw an exception.)

The attentive reader will have noticed that these are opposites. A neat trick, to have an operator which means two contradictory things, don’t you think?

显然您是我召集的“细心的读者”之一,他们注意到我们有一个操作在逻辑上意味着两个截然不同的事情。这是一个很好的观察!

你的问题是“为什么会这样?”这不是一个好问题! :-)

正如我在本网站上多次 提到的那样,我无法满意地回答“为什么”问题。 “因为那是规范所说的”是一个正确的答案,但并不令人满意。真正提问者通常寻找的是语言设计过程的总结。

当 C# 语言设计团队设计功能时,争论可能会持续几个月,他们可能会涉及十几个人讨论许多不同的提案,每个提案都有自己的优缺点,从而产生数百页的笔记。即使我从 1990 年代后期的会议上获得了有关演员表操作的相关信息(我没有),似乎也很难以令原始提问者满意的方式对其进行简洁的总结。

而且,要圆满回答这个问题,当然要从整个历史的角度来讨论。 C# 旨在让现有的 C、C++ 和 Java 程序员立即高效工作,因此它借鉴了这些语言的许多约定,包括转换运算符的基本机制。为了正确回答这个问题,我们还必须讨论 C、C++ 和 Java 中转换运算符的历史。在 Whosebug 上的答案中,这似乎包含太多信息。

坦率地说,最可能的解释是这个决定不是不同立场的长处辩论的结果。相反,语言设计团队可能考虑了如何在 C、C++ 和 Java 中完成,做出了一个看起来不太糟糕的合理妥协立场,然后转向其他更有趣的业务。因此,一个正确的答案几乎完全是历史性的;为什么 Ritchie 像他为 C 所做的那样设计转换运算符?我不知道,也不能问他

我对你的建议是不要再问“为什么?”有关编程语言设计历史的问题,而是询问有关特定代码的特定技术问题,这些问题的答案很简短。