C# 编译时未捕获强制转换错误

C# 编译时未捕获强制转换错误,c#,C#,我知道基类不能转换成派生类。我不明白的是,为什么在编译时没有捕捉到这一点?例如: class GradeBook { } class ProfessorGradeBook : GradeBook { } class Program { static void Main(string[] args) { ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); //shouldn

我知道基类不能转换成派生类。我不明白的是,为什么在编译时没有捕捉到这一点?例如:

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?
    }
}
public class A {}
public class B {}

A a = new B();
B b = (B)a;
我已经看了关于stackoverflow的其他问题,但我仍然不明白为什么会编译这个<代码>教授成绩册a=(教授成绩册)新成绩册()在任何情况下都不会成功(对吗?),那么为什么这是一个运行时错误而不是编译时错误呢

编辑:

我已经知道为什么编译器永远不会捕捉到这一点:

GradeBook a = new ProfessorGradeBook();
ProfessorGradeBook b = (ProfessorGradeBook)a; 
ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();
在运行时,
a
可能指向任何东西,因此编译器应该信任您。我更关心的是,为什么编译器永远不会捕捉到这一点:

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

我想最有意义的答案是Eric Lippert的第一句评论,特别是“绝大多数开发人员永远不会键入那行代码”,因此编译器团队从来没有考虑过尝试将其作为错误。

你是对的,它在实践中永远不会成功,但是
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?
}
那更难。我们可以任意使它变得困难。

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

例如:

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?
    }
}
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
,您不关心编译时的派生类型元数据,任何向下转换都将在运行时进行评估,因为编译器无法证明向下转换,除非执行代码,并且运行时发现由于显式提供的类型不属于源类型层次结构的一部分


可能编译器能够捕获无效的降级,但这也会增加构建时间…

这不是编译问题,因为编译器将这两个命令分开。首先,它创建了一个新的成绩册实例,然后他尝试从基础对象中投射一个对象,这也很好。编译器并不“知道”这个对象实际上只是一个基本对象。简言之:编译器团队可能会花费时间和精力将其作为警告或错误。花费有限的精力的目的是解决实际开发人员实际遇到的实际问题。一个明确的非目标是在编译时发现任何可能的错误。编译器很容易对此发出警告。这并不是因为绝大多数开发人员根本就不会首先键入。一个永远不会触发的警告,因为没有人键入该代码是浪费精力的,可以花在设计更有用的警告上;这意味着“编译器注意,我正在编写您无法证明在这里是类型安全的代码。我知道它是类型安全的;相信我,并允许此代码进行编译。如果我错了,请崩溃我的程序”。也就是说,强制转换显式关闭了安全系统。当您关闭错误检测系统时,编译器不会因为没有给出错误而受到指责!编写强制转换时,您负责确保强制转换成功,而不是编译器!