2012-09-09 9 views
8

クラスからインターフェイスへの無効なキャストを試みると、コンパイラはエラーにはなりません(エラーは実行時に発生します)。しかし、抽象クラスに似たようなキャストを試みるとになります。コンパイラは、それは(一見?)無効だIBAR、はFooからキャストを拒否しないのはなぜクラスを実装していないインタフェースにクラスをキャストするときにコンパイラエラーが発生しないのはなぜですか?

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'はprivateにネストされているので、キャストを許さなければならない場合でも、C#コンパイラは_warning_を与えることができますしかし、それはおそらくまれなケースです。)_編集:_これは、以下のAlexeiの対象です。 –

答えて

7

これはなぜ意味があるのか​​を知るためには、.Netの継承システムを理解する必要があります。 .NETでは、クラスは1つの基本クラスから継承することができますが、任意の数のインタフェースを実装できます。問題の非常に簡単なオリジナルの例では

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のこのインスタンスが決してIBARにキャスト可能になるだろうされていないことを検出することができたコンパイラのように思えるが、それは警告を「あると便利」の詳細です言語の正しさの問題よりも。

:コンパイラは、他のクラスは、それはコンパイル時にインタフェースにキャスト失敗するクラスから派生することはできないことを証明できた場合:
+1

+1。また、コンパイル時にキャストが失敗した場合を示す回答も追加されました。 –

+0

FooをBarにキャストしたいと思ったら、BarableFooに最初にキャストしなければならないと思っていましたので、あなたが期待していたタイプを明確にしてください。 – Andy

+0

@Andy - あなたは最後の行か、ミドル '(IBar)foo'? – BitwiseMan

6

キャストの全体的なポイントは、コンパイラエラーを抑制することです。
(例えば、あなたがfooが実際にインタフェースを実装していますサブタイプのインスタンスであることがわかっている場合)

コンパイラはキャストが成功することは不可能であることを証明できれば、それはまだエラーが発生します。 (クラス(階層内にない)にキャストした場合)

+0

Ok ...コンパイラが最初のキャストが無効であることを証明できると言っていますが、2番目のキャストは無効です。しかし、なぜそれが第2を証明することができないのですか? Foo(またはその祖先の1人)は、そのキャストを可能にするためにIBarを実装すると宣言しなければならないのですか? – McGarnagle

+1

@dbaseman: 'class Baz:Foo、IBar'と書くとどうなりますか? – SLaks

+1

@McGarnagle: 'Foo'の子孫の1人は、キャストを可能にするために' 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自体を実装していないことを知っているため、コンパイル時に失敗する可能性があります。 2番目のケース(NoBarValue)は、値の型(構造体)が暗黙的にシールされている点と似ています。

+0

よかったです、ありがとうございました。 – McGarnagle

関連する問題