2012-12-11 7 views
27

contravariantデリゲートをキャストしようとしていますが、何らかの理由で "as"演算子を使ってのみ行うことができます。私はcontravariantデリゲートを "as"でキャストすることができます

interface MyInterface { } 
delegate void MyFuncType<in InType>(InType input); 

class MyClass<T> where T : MyInterface 
{ 
    public void callDelegate(MyFuncType<MyInterface> func) 
    { 
     MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error 
     MyFuncType<T> castFunc2 = func as MyFuncType<T>; 
     MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error 
    } 
} 

castFunc2正常に動作しますが、castFunc1とエラーが発生しcastFunc3:

Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>' 

castFunc2とcastFunc3ので、私はそれらのいずれかが原因となる可能性がどのようにだけ理解していない「同等」ですMSDN article on the as operator状態エラー。これを混乱させるもう一つの問題は、MyInterfaceをインタフェースからクラスに変更するとエラーを取り除くことです。

ここで何が起こっているのか理解してくれる人はいらっしゃいますか? ありがとう!

+2

これは私のためにここでうまくコンパイルされます:http://ideone.com/5SjUxV私は何か不足していますか? (最も理想的なものではない)。私は、 'MyFuncType'宣言から' in'を取り除くと、エラーが発生します。 –

+0

hmm。イデオンはモノを使用していますが、私はビジュアルスタジオからモノを使ってみましたが、まだエラーが出ています。 – rob

+0

@rob私はモノ・ターゲティング.Net 4を使用し、エラーは発生しませんでした。どのバージョンの.Netフレームワークをターゲットにしていますか? –

答えて

15

Tがクラスでなければならないような制約を追加します。

class MyClass<T> where T: class, MyInterface 

これは、Tが変換可能であることを知るための十分な情報をコンパイラに提供します。明示的なキャストは必要ありません。

差異は参照タイプにのみ適用されます。 Tは、制約がない値型であることが許されているため、コンパイラは、Tが反分散に対して互換性があることを証明する能力を壊す。

asは実際にはヌル変換を実行できるため、2番目のステートメントが機能するのは理由です。たとえば:

class SomeClass { } 
interface SomeInterface { } 
static void Main(string[] args) 
{ 
    SomeClass foo = null; 
    SomeInterface bar = foo as SomeInterface; 
} 

Fooは明らかにSomeInterfaceに直接変換可能ではありませんが、ヌル変換がまだ起こることができるので、それはまだ成功します。 MSDNのリファレンスはほとんどのシナリオでは正しいかもしれませんが、生成されたILコードは非常に異なります。つまり、技術的な観点からは根本的に異なります。

+1

+1。 'MyFuncType castFunc1 =(MyFuncType )(object)func;'しかし、これは実行時に値型のために爆発するので、これがより良い解決策です。 –

+1

**良い説明。**あなたの最後の例について:ここでは、ジェネリックスなしで、明示的なキャストもコンパイルされます。つまり、 'bar =(SomeInterface)foo'はコンパイル時に許可されます。しかし、 'T'のような一般的なパラメータが含まれている場合、コンパイラはより厳密です。これは、ジェネリック医薬品を誤って使いすぎて過度のキャスティングをしないようにするためです。 'foo' /' bar'の例では 'SomeClass'が開封されていることが非常に重要であることに注意してください。 'sealed'の場合、コンパイラは' SomeClass'が決して 'SomeInterface'ではないことを知ります。 –

-1

MyInterfaceはインスタンスタイプではないため、TMyInterfaceを実装しています。したがって、MyFuncType<T>MyFuncType<MyInterface>であるとは限りません。 MyFuncType<SomeType>SomeType : MyInterfaceになる可能性がありますが、それはSomeOtherType : MyInterfaceと同じではありません。理にかなっている?

+0

"' MyFuncType 'は' MyFuncType 'であることは保証されていません"それは本当ですが、あなたは割り当てとキャスティングを混同していると思います。 –

+0

編集していただきありがとうございます。私はそれを台無しに気づいていない。ここでキャスティングと割り当てに混乱はありません。私の指摘は、ジェネリックはテンプレートであり、 'T'は' MyInterface'であることは決して保証されないということです。 – Jake

4

Eric Lippertさんが最近の投稿でAn "is" operator puzzle, part oneAn "is" operator puzzle, part twoというこの問題について説明しました。

この動作の主な根拠は以下のとおりです。「is」(または「as」)演算子はキャストと同じではありません。 "as"演算子は、対応するキャストが不正である場合にnull以外の結果イベントを発生させる可能性があります。これは、型引数を扱う場合に特に当てはまります。

(エリックが言ったように)基本的には、あなたのケースでcastオペレータは、「「私は、コンパイラは、コンパイラがそれを可能にしなければならない、ということを知りませんが、この値は指定されたタイプであることを知っている」またはことを意味私はこの値が与えられた型のものではないことを知っています;特殊型のコードを生成して、ある型の値を別の型の値に変換します。「

後者の場合は、ダブル・ツー・整数変換のような値の変換を扱う、我々は現在のコンテキストでこの意味を無視することができます。

をそして、あなたが扱っている場合は、ジェネリック型の引数はどちらも。最初のコンテキストにおける論理的ではありませんジェネリック型引数を使用してこの「契約」を明確に述べていない理由よりも

私はあなたが達成したいことを100%確信していませんが、特別な型を省略することができます代わりに汎用引数を自由に使用できます。

class MyClass<T> where T : MyInterface 
{ 
    public void callDelegate(Action<T> func) 
    { 
    } 
} 

class MyClass2 
{ 
    public void callDelegate<T>(Action<T> func) 
     where T : MyInterface 
    { 
    } 
} 

そうでない場合は、as operator with type checkの代わりにnullをチェックする必要があります。

関連する問題