据我所知,基类无法转换为派生类。我不明白为什么在编译时没有抓到它?例如:
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?
}
}
我已经查看了有关stackoverflow的其他问题,但它仍然没有理由为什么这会编译? ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();
在任何情况下都不会成功(对吧?)那么为什么这是运行时错误而不是编译时错误?
编辑:
我已经知道为什么编译器永远不会抓住这个:
GradeBook a = new ProfessorGradeBook();
ProfessorGradeBook b = (ProfessorGradeBook)a;
在运行时,a
可能指向任何内容,因此编译器应该只信任您。我更关心为什么编译器永远不会抓住这个:
ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();
我认为最有意义的答案是Eric Lippert的第一个评论,特别是"绝大多数开发人员永远不会输入这行代码"因此,编译器团队从不关心尝试使其成为错误。
答案 0 :(得分:1)
这是 downcast 。编译器无法知道较少类型的引用是否可以可转换到更专业的类型。
例如:
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
并且您不关心编译时的派生类型元数据,并且任何向下转换都将在运行时进行评估,因为编译器不能除非代码被执行并且运行时发现所谓的向下转换不可能,因为明确提供的类型不是源类型层次结构的一部分,所以证明是向下转换。
编译器可能能够捕获无效的向下转发,但这也可能会增加构建时间......
答案 1 :(得分:0)
你是对的,它在实践中永远不会成功,但是new
指令是一个运行时指令,而不是一个编译时指令,你正在做一个显式的转换(ProfessorGradeBook),这基本上是对编译器说:"嘿编译器,只要相信我它就会工作"。
所以编译器会这样做。
在某些情况下,可以使用Fody或PostSharp之类的东西在编译后添加转换运算符
答案 2 :(得分:0)
编译器无法捕获所有静态可识别的错误。这就陷入了停滞不前的问题。编译器捕获所有静态可发现错误的明确定义的子集。
另请注意,不允许编译器任意智能。 C#语言规范说明了它必须具有多么智能,以便所有C#编译器的行为方式相同。
你喜欢它有多聪明?你想要它也能抓住这个吗?
static void Main(string[] args)
{
var g = new GradeBook();
ProfessorGradeBook a = (ProfessorGradeBook)g;
//shouldn't this be a compile time error?
}
那更难了。我们可以任意努力。