8

これが機能しないのはなぜですか?デリゲートの共分散を正しく理解していませんか?

public delegate void MyDelegate(object obj)

public class MyClass
{
    public MyClass()
    {
         //Error: Expected method with 'void MyDelegate(object)' signature
         _delegate = MyMethod;
    }

    private MyDelegate _delegate;

    public void MyMethod(SomeObject obj)
    {}

}
4

6 に答える 6

12

正解-共分散を正しく理解していません-まだ:)同じタイプであるが戻り値として次のようになっている場合、コードは機能します。

public delegate object MyDelegate()

public class MyClass
{
    public MyClass()
    {
         _delegate = MyMethod;
    }

    private MyDelegate _delegate;

    public SomeObject MyMethod() { return null; }
}

それは共分散を示します。または、パラメータとして保持し、タイプを切り替えることもできます。

public delegate void MyDelegate(SomeObject obj)

public class MyClass
{
    public MyClass()
    {
         _delegate = MyMethod;
    }

    private MyDelegate _delegate;

    public void MyMethod(object obj) {}
}

これは、共変性を示しています。

私の経験則は、「デリゲートが与えられたら、それで何ができるか?メソッドを壊す引数を渡すことができれば、変換は失敗するはずです。メソッドが何かを返すことができれば、発信者、変換は失敗したはずです。」

あなたのコードでは、あなたは次のように呼ぶことができたでしょう:

_delegate(new object());

その時点で、poorには、型であることが意図されているが、実際には型でMyMethodあるパラメータがあります。これは非常に悪いことになるので、コンパイラはそれが起こらないようにします。SomeObjectobject

それはすべてもっと理にかなっていますか?

于 2010-02-26T21:05:48.527 に答える
4

引数は反変であり、戻り値の型は共変です。objectのインスタンスではないでデリゲートが呼び出された場合SomeObject、入力エラーが発生します。一方、戻るSomeObjectデリゲートにラップされたルーチンから戻ることobjectは問題ありません。

于 2010-02-26T21:06:59.013 に答える
3

ジェネリックを使用する必要があります。

編集:なぜですか?別の投稿者が指摘したように、ObjectとSomeObjectは、ObjectがSomeObjectではない可能性があるのと同じものではないためです。これが、この言語におけるジェネリックスの要点です。

public delegate void MyDelegate<T>(T obj)

public class MyClass
{
    public MyClass()
    {
        _delegate = MyMethod;
    }

    private MyDelegate<SomeObject> _delegate;

    public void MyMethod(SomeObject obj)
    {
    }
}
于 2010-02-26T21:05:56.007 に答える
1

タイプは、任意のMyDelegate種類のオブジェクトを渡すことができることを宣言します。ただし、MyMethodタイプのオブジェクトのみを受け取りますSomeObject。別の種類のオブジェクトを渡してデリゲートを呼び出そうとするとどうなります_delegate("a string object")か?の宣言によるとMyDelegate、これは許可されるべきですが、関数MyMethodは実際には文字列引数を受け取ることができません。

于 2010-02-26T21:05:28.660 に答える
1

提供したMSDNリンクから

共分散により、メソッド はデリゲートで定義されているものよりも派生したリターンタイプを持つことができます。 共変性は、デリゲート型よりも派生が少ないパラメーター型を持つメソッドを許可し ます。

サポートされていない、より派生したパラメータータイプを使用しようとしています(ただし、.NET 4.0は、多くの共分散/反変性の問題を解決したため、おそらく使用されます)。

于 2010-02-26T21:08:10.557 に答える
0

共変性と反変性は、継承の原理を理解することです。

共分散と反分散の両方で、s.th。戻り値またはデリゲート メソッドへの引数として「渡されます」。「受け継がれる」ものは、​​受け皿に「捉える」必要があります。C# (またはプログラミングの専門用語など) では、私がレセプタクルと呼んだものに対してバケットという言葉を使用します。よく使われる専門用語の意味を理解するために、他の言葉に頼らなければならないことがあります。

とにかく、ここの読者なら誰でも継承を理解しているのであれば、注意すべき唯一のことは、レセプタクル、つまりキャッチに使用されるバケツは、同じタイプか、取得されているものよりも派生タイプが少ない必要があるということです。合格 - これは、共分散と反分散の両方に当てはまります。

継承は、鳥が動物であるため、動物のバケツで鳥を捕まえることができると言います。したがって、メソッドのパラメーターが鳥を捕まえる必要がある場合、それをアニマル バケット (アニマル タイプのパラメーター) でキャッチできます。これは反変性です。また、メソッド、つまりデリゲートが鳥を返す場合、「バケット」も鳥型または下位派生型 (親型) にすることができます。つまり、メソッドの戻り値をキャッチする変数は、戻り値と同じかそれ以下の派生型。

渡されたものとキャッチされたものを区別するように思考を切り替えるだけで、共分散と反分散に関するすべての複雑さがうまく解消されます。すると、同じ原理が働いていることがわかります。ただ、継承は一方向にしか流れないので破ることはできません。

また、コンパイラは非常にスマートであるため、バケットをより特殊化された型にキャストすると (必要に応じて再度)、より派生したクラスに追加されたすべての特殊化されたメソッドが返されます。それはそれの美しさです。したがって、それはあなたが持っているもの、おそらく必要なものをキャッチし、キャストし、使用することです.

于 2015-07-03T12:52:58.010 に答える