2012-09-09 18 views
8

如果我尝试从类到接口的无效转换,那么编译器不会抱怨(错误发生在运行时);它确实抱怨,但是,如果我尝试类似演员抽象类。为什么没有编译器错误,当我将一个类转换为它没有实现的接口?

class Program 
{ 
    abstract class aBaz 
    { 
     public abstract int A { get; } 
    } 

    interface IBar 
    { 
     int B { get; } 
    } 

    class Foo 
    { 
     public int C { get; } 
    } 

    static void Main() 
    { 
     Foo foo = new Foo(); 

     // compiler error, as expected, since Foo doesn't inherit aBaz 
     aBaz baz = (aBaz)foo; 

     // no compiler error, even though Foo doesn't implement IBar 
     IBar bar = (IBar)foo; 
    } 
} 

为什么不编译器拒绝来自伊巴尔投,当它的(貌似?)无效?或者,如果编译器允许这个“无效”强制转换到接口IBar,为什么它不允许类似的“无效”强制转换为抽象类aBaz

+1

http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx – SLaks

+0

这个铸造到一个接口位在我今晚的对接......和男孩它受伤了。 –

+1

您没有将'Foo'标记为'sealed'。因此,明确的演员应编制。可能存在一个从'Foo'继承的类,它还执行'IBar'。如果你使用'密封类Foo',在编译时对'IBar'的转换是非法的。 (在这种情况下,类Foo是嵌套的,所以即使必须允许强制转换,也可以设想C#编译器可以给出_warning_(不是错误,因为该规范说显式转换存在),因为它可以看到嵌套类的全部范围,但这可能是一种罕见的情况。)_编辑:_我现在看到这在下面的阿列克谢覆盖。 –

回答

7

您需要了解.Net的继承系统以了解为什么这是有道理的。在.Net中,类可以只从一个基类继承,但可以实现任意数量的接口。

class Program 
{ 
    abstract class aBaz 
    { 
     public abstract int A { get; } 
    } 

    interface IBar 
    { 
     int B { get; } 
    } 

    class Foo 
    { 
     public int C { get; } 
    } 

    class BarableFoo : Foo, IBar 
    { 
     public int C { get; } 
    } 

    static void Main() 
    { 
     // This is why the compiler doesn't error on the later cast 
     Foo foo = new BarableFoo(); 

     // compiler error: aBaz is a class and the compiler knows that 
     // Foo is not a _subclass_ of aBaz. 
     aBaz baz = (aBaz)foo; 

     // no compiler error: the class Foo does not implement IBar, however at runtime 
     // this instance, "foo", might be a subclass of Foo that _implements_ IBar. 
     // This is perfectly valid, and succeeds at runtime. 
     IBar bar = (IBar)foo; 

     // On the other hand... 
     foo = new Foo(); 

     // This fails at runtime as expected. 
     bar = (IBar)foo; 
    } 

} 

在这个问题非常简单原始的例子,它看起来像编译器可以检测到FOO的这个实例是永远不会被强制转换为伊巴尔,但更多的是“锦上添花”,警告而不是语言的正确性。

+1

+1。还添加了在编译时失败时显示案例的答案。 –

+0

我本来料想如果你想把一个Foo加入到一个Bar中,你必须先将它投射到BarableFoo,这样它才能清楚你所期望的是什么类型 – Andy

+0

@Andy - 你是在谈论最后一行还是中间'(IBar)foo'? – BitwiseMan

6

一个强制转换的重点是抑制编译器错误。
(例如,如果你知道foo实际上是没有实现接口的子类型的实例)

如果编译器可以证明它是不可能的投成功,它仍然会给出一个错误。 (例如,如果投到类别不在其层次结构中)

+0

好的...所以你说编译器可以证明第一个演员是无效的,但不是第二个演员。但为什么它不能证明第二个呢? Foo(或其祖先之一)不得不被宣布为实施IBar以使该演员成为可能? – McGarnagle

+1

@dbaseman:如果我写'class Baz:Foo,IBar'会怎么样? – SLaks

+1

@McGarnagle:其中一个'Foo'的后代必须实现'IBar'才能进行转换,但在很多情况下,编译器无法知道包含'Foo'后代的程序集是否实现了' IBar'可能会被加载。 – supercat

5

,并表明编译器不傻存在时投将在编译时失败actulaly情况:如果编译器可以证明没有其他类可以从它会在编译时失败铸造接口类派生:

sealed class NoBar 
{ 
} 

struct NoBarValue 
{ 
} 

IBar noBar = (IBar)(new NoBar()); // fails at compile time 
IBar noBarValue = (IBar)(new NoBarValue()); // fails at compile time 

在第一种情况下(NoBar)类被明确地密封(因此没有派生类可以实现IFoo),编译器知道它本身没有实现IBar - 因此可能会在编译时失败。第二种情况(NoBarValue)类似,只是值类型(结构)隐式密封。

+0

好的补充,谢谢。 – McGarnagle

相关问题