【问题标题】:Cast error not caught at compile-time [duplicate]编译时未捕获转换错误[重复]
【发布时间】:2015-12-12 23:22: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?
    }
}

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

编辑:

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

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

在运行时,a 可能指向任何东西,因此编译器应该信任您。我更关心为什么编译器永远不会捕捉到这个:

ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook();

我想最有意义的答案是 Eric Lippert 的第一条评论,特别是“绝大多数开发人员永远不会键入那行代码”,因此编译器团队从不担心试图让那行代码出错。

【问题讨论】:

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

标签: c#


【解决方案1】:

这是一个悲观。编译器无法知道类型较少的引用是否可以强制转换到更专业的类型。

例如:

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 因为您提供的假设是可以使用显式转换。 p>

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

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

【讨论】:

    【解决方案2】:

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

    编译器也是如此。

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

    【讨论】:

      【解决方案3】:

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

      还要注意,编译器是不允许任意聪明的。 C# 语言规范说明了它必须有多智能才能使所有 C# 编译器的行为方式都相同。

      你喜欢它有多聪明?你想让它也捕捉到这个吗?

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

      这更难。我们可以任意设置难度。

      【讨论】:

        猜你喜欢
        • 2014-07-09
        • 2018-08-02
        • 1970-01-01
        • 2016-11-10
        • 1970-01-01
        • 1970-01-01
        • 2019-01-13
        • 2019-01-17
        • 1970-01-01
        相关资源
        最近更新 更多