编译时未捕获转换错误

本文关键字:转换 错误 编译 | 更新日期: 2023-09-27 18:26:37

我知道基类不能转换为派生类。我不明白的是为什么在编译时没有捕获?例如:

class GradeBook
{
}
class ProfessorGradeBook : GradeBook
{
}
class Program
{
    static void Main(string[] args)
    {
        ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); 
        //shouldn't this be a compile time error?
    }
}

我已经查看了有关堆栈溢出的其他问题,但对我来说仍然没有意义,为什么要编译? ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();在任何情况下都不会成功(对吧?(,那么为什么这是运行时错误而不是编译时错误呢?

编辑:

我已经知道为什么编译器永远不会抓住这个:

GradeBook a = new ProfessorGradeBook();
ProfessorGradeBook b = (ProfessorGradeBook)a; 

在运行时,a可能指向任何内容,因此编译器应该只信任您。我更关心的是为什么编译器永远不会抓住这个:

ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();

我想最有意义的答案是 Eric Lippert 的第一条评论,特别是"绝大多数开发人员永远不会输入那行代码",所以编译器团队从不关心试图让它成为错误。

编译时未捕获转换错误

这是一个

沮丧的。编译器无法知道类型较少的引用是否可以强制转换为更专业的类型。

例如:

public class A {}
public class B {}
A a = new B();
B b = (B)a;

a 的类型为 A,而存储在那里的对象类型为 B 。顺便说一句,当你尝试将其转换为B时,因为A的实例可以A本身或任何派生的A类(包括B,但不仅仅是B(,编译器不能假设a不能投射B,因为你提供的假设是可以使用显式强制转换。

归根结底,键入是元数据。如果你将引用声明为 A,你就是在告诉编译器,无论你设置什么,都会有A,假设你丢失了编译时元数据,无法从可能的派生类型访问更专业的成员。换句话说:你告诉编译器引用是A的,你不关心编译时的派生类型元数据,任何下行转换都将在运行时被计算,因为编译器无法证明下放,除非执行代码,并且运行时发现所谓的向下转换是不可能的,因为显式提供的类型不是源类型层次结构的一部分

可能编译器能够捕获无效的向下转换,但这也可能增加构建时间......

你是对的,它在实践中永远不会成功,但new指令是运行时指令,而不是编译时指令,你正在对(ProfessorGradeBook(进行显式转换,这基本上是对编译器说:"嘿编译器,相信我它会工作的"。

编译器也是如此。

在某些情况下,可以使用Fody或PostSharp之类的东西在编译后添加转换运算符。

编译器不会捕获所有静态可知的错误。这遇到了停止问题。编译器捕获所有静态可发现错误的明确定义的子集。

另请注意,编译器不允许任意智能。C# 语言规范说明了它必须有多智能,以便所有 C# 编译器的行为方式相同。

你喜欢它有多聪明?你也想让它抓住这个吗?

static void Main(string[] args)
{
    var g = new GradeBook();
    ProfessorGradeBook a = (ProfessorGradeBook)g; 
    //shouldn't this be a compile time error?
}

那就更难了。我们可以让它任意困难。