2009-10-09 55 views
0

当我编译下面的代码,我只看到在运行时它说 错误“无法投型‘Foo1’的对象键入‘foo2的’”为什么只有在将基类对象转换为dervied类对象时出现运行时错误?

为什么编译器不会显示在此错误编译时间?

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 {} 
+0

对不起。我无法正确格式化代码块。为什么有猜测? – csharpbaby 2009-10-09 04:42:06

+0

好的。我使用代码格式化程序。现在代码看起来很好:) – csharpbaby 2009-10-09 04:43:07

+0

对不起,在阅读你的第一条评论之后,试图帮助格式化代码。 – David 2009-10-09 04:44:19

回答

2

编译器是不够聪明,知道objFoo1确实是一个简单的Foo1。它没有足够深入地分析你的源代码来确定它。它看到的全部是objFoo1Foo1,Foo2是从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

它因为编译器正在服从您的指令进行强制转换。然后它将允许编译代码。

Foo2 objFoo2 = (Foo2)objFoo1; 

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

Foo2 objFoo2 = objFoo1; // kaboom 

无论如何,真正的原因是,你的实例化对象Foo1,这是foo2的基地,而不是foo2的。

,如果你做这种方式,它会工作

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

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

记住多态性允许引用派生类型作为其基本类型的实例 - 只知道和关心的编译器是参考声明的类型 - 在这种情况下,参考是指潜在的有效投故允许。

0

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

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

相关问题