2012-09-09 40 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

相關問題