为什么只有在将基类对象转换为派生类对象时出现运行时错误?

时间:2009-10-09 04:40:51

标签: c#-3.0

当我编译以下代码时,我只在运行时看到错误  “无法将”Foo1“类型的对象强制转换为”Foo2“

为什么编译器在编译期间没有显示此错误?

public void Start()
{
     Foo1 objFoo1 = new Foo1();
     Foo2 objFoo2 = (Foo2)objFoo1;

     //objFoo1.FooA = 10;
     //Console.WriteLine(objFoo2.FooA);

}

public class Foo1    {}
public class Foo2 : Foo1    {}

4 个答案:

答案 0 :(得分:2)

编译器不够聪明,不知道objFoo1实际上是一个简单的Foo1。它不会深入分析您的源代码以确定它。它只看到objFoo1Foo1Foo2来自Foo1,因此演员表是合法的。如果改为将其更改为(int) objFoo1,它会在编译时弹出,因为编译器可能会发现没有可能的方法将Foo1转换为整数。

想象一下,两个声明之间有1000行代码,其中许多代码对objFoo1进行了各种分配。编译器必须做很多努力才能尝试确定objFoo1中究竟是什么对象。一般来说,它并不总是能够这样做。编译器只需在面值处获取静态类型信息并假设objFoo1是某种类型的Foo1对象而不是更多。这样它就不会拒绝你的简单测试程序,而是接受一个更复杂的程序。

在这种情况下,拒绝代码也不是编译器的工作。尽管不可否认,你有可能通过非法演员有意尝试生成ClassCastException。这样做是不寻常的,但并非违法。

在这些方面,还有其他情况下编译器的静态代码分析可能被欺骗。这将无法编译:

return;
System.out.println("hello world!"); // compile error - unreachable code

虽然它在语义上是相同的,但编译得很好:

if (true) return;
System.out.println("hello world!");

答案 1 :(得分:1)

没有编译时错误,因为可能你的代码可以工作,因为objFoo1实际上可能是Foo2类型 - 编译器根本不知道在执行时,对象的实际类型可能不同。

请记住,多态性允许您引用派生类型作为其基类型的实例 - 编译器只知道并关心引用的声明类型 - 在这种情况下,引用表明潜在有效这是允许的。

答案 2 :(得分:0)

Foo2是Foo1。 Foo1不是Foo2,但这是演员试图断言的内容。现实生活的比喻是:通常,孩子从父母那里继承钱,但父母不会从子女那里继承钱。

我觉得你的痛苦在这里。当我第一次开始OOP时,我遇到了很多这类问题,我花了一些时间来绕过一些细微差别。如果我有一个Foo1并且我希望它获得添加的功能,为什么我不能只是向另一个方向投射并获得那些额外的属性和方法?不幸的是,这不是它的工作原理。然而,随着时间和练习,它会变得更容易。

答案 3 :(得分:-1)

因为编译器遵守你的指令。然后它将允许编译代码。

Foo2 objFoo2 = (Foo2)objFoo1;

如果你不强迫它,它会给你一个编译错误。

Foo2 objFoo2 = objFoo1; // kaboom

无论如何,真正的原因是你实例化对象Foo1,它是Foo2的基础​​,但不是Foo2。

如果你这样做,它会起作用

Foo1 foo1 = new Foo2();
Foo2 foo2 = foo1 as Foo2;