28

共変性のデリゲートをキャストしようとしていますが、何らかの理由で「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>'

as演算子に関するMSDNの記事には、castFunc2とcastFunc3は「同等」であると記載されているため、どちらか一方だけがエラーを引き起こす可能性があるのか​​わかりません。私を混乱させているもう1つの部分は、MyInterfaceをインターフェイスからクラスに変更するとエラーがなくなることです。

誰かが私がここで何が起こっているのかを理解するのを手伝ってもらえますか?ありがとう!

4

3 に答える 3

15

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

class MyClass<T> where T: class, MyInterface

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

差異は参照型にのみ適用されます。Tは、Tが反変性と互換性があることを証明するコンパイラーの能力を損なう制約のない値型であることが許可されています。

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

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

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

于 2012-12-11T05:54:05.273 に答える
4

Eric Lippertは、最近の投稿でこの問題について優れた説明をしました。「is」演算子パズル、パート1「is」演算子パズル、パート2

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

基本的に、castあなたの場合の演算子は、(エリックが言ったように)「この値が指定されたタイプであることを知っていますが、コンパイラはそれを知らないのに、コンパイラはそれを許可する必要があります」または「この値は指定されたタイプ;あるタイプの値を別のタイプの値に変換するための特別な目的のタイプ固有のコードを生成します。」

後のケースでは、doubleからintegerへの変換などの値の変換を扱いますが、現在のコンテキストではこの意味を無視できます。

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

何を達成したいかは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、タイプチェックの代わりにnullのチェックで演算子を使用する必要があります。

于 2012-12-11T06:54:21.417 に答える
-1

あなたのクラスは、インスタンスタイプではないのでそれをT実装すると言っています。したがって、であることが保証されていません。とである可能性がありますが、それはと同じではありません。わかる?MyInterfaceMyInterfaceMyFuncType<T>MyFuncType<MyInterface>MyFuncType<SomeType>SomeType : MyInterfaceSomeOtherType : MyInterface

于 2012-12-11T04:14:19.750 に答える