编译时未捕获转换错误
本文关键字:转换 错误 编译 | 更新日期: 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?
}
那就更难了。我们可以让它任意困难。