8

パラメータの1つとしてジェネリック型のデリゲートがあります。

public delegate void UpdatedPropertyDelegate<T>(
    RemoteClient callingClient, 
    ReplicableProperty<T> updatedProp, 
    ReplicableObject relevantObject
);

今、私は他のクラスが使用するためにサブスクライブできる公開イベントが欲しいです。したがって、私はしました:

public event UpdatedPropertyDelegate<T> UpdatedProperty;

ただし、コンパイラはそれを好みません。ここでTを指定する必要がある理由がわかりません。確かに、イベントを発生させるときに指定されます。つまり、次のようになります。

if (UpdatedProperty != null) 
{
    UpdatedProperty(this, readProperty, 
        ReplicableObjectBin.GetObjectByID(readProperty.OwnerID));
}

それで、私は何か単純な間違ったことをしていますか?それとも、これは理解の大きな失敗ですか?

ありがとう。

4

3 に答える 3

8

必要なのは、デリゲートではなく、インターフェイスタイプのようです。デリゲートは受け入れられませんが、インターフェースメソッドはオープンジェネリック型(あなたが求めているもの)を受け入れることができます。たとえば、次のように定義できます。

interface ActOnConstrainedThing<CT1,CT2>
{
  void Act<MainType>(MainType param) where MainType: CT1,CT2;
}

CT1との実装者がとを実装CT2する共通のベースタイプを共有しない場合でも、の実装はCT1CT2渡さActれたパラメーターを型キャストとして、CT1または型キャストなしで使用でき、制約付きCT2の汎用パラメーターを期待するルーチンに渡すこともできます。そのようなことは、代表者では不可能です。CT1CT2

デリゲートではなくインターフェースを使用するということは、通常の「イベント」メカニズムと構文を使用できないことを意味することに注意してください。代わりに、イベントパブリッシャーとなるオブジェクトは、目的のインターフェイス(aList<ActOnConstrainedThing<IThis,IThat>>など)を実装するオブジェクトインスタンスのリストを維持し、そのリストのインスタンスを列挙する必要があります(おそらくを使用してforeeach)。例えば:

List <IActOnConstrainedThing <IThis、IThat >​​> _ActOnThingSubscribers;

void ActOnThings <T>(T param)ここで、T:IThis、IThat
{{
  foreach(_ActOnThingSubscribersのvarthing)
  {{
    things.Act <T>(param);
  }
}

編集/補遺

私がこのパターンを採用した場所には、質問にあまり関連していないように見える他のいくつかのものもありました。私の解釈では、オープンタイプのパラメーターを持つデリゲート(または同等のもの)をどのように持つことができるかを尋ねていたので、オブジェクトはデリゲートと同等のものは、デリゲートを提供するオブジェクトが事前にそれを知る必要なしに、タイプパラメータを提供できます。これが役立つほとんどの場合、一般的な制約が含まれますが、それは明らかに混乱を招いたため、次のような例はありません。

インターフェイスIShuffleFiveThings
{{
  void Shuffle <T>(ref T p1、ref T p2、ref T p3、ref T p4、ref T p5);
}
List <IShuffleFiveThings _ShuffleSubscribers;

void ApplyShuffles <T>(ref T p1、ref T p2、ref T p3、ref T p4、ref T p5)
{{
  foreach(_ShuffleSubscribersのvar shuffler)
  {{
    things.Shuffle(ref p1、ref p2、ref p3、ref p4、ref p5);
  }
}

このIShuffleFiveThings.Shuffle<T>メソッドは、5つのパラメーターを受け取り、それらを使用refして何かを実行します(おそらく、何らかの方法でパラメーターを並べ替えます。ランダムに並べ替えるか、ランダムに並べ替えて、他のパラメーターをそのままにします。リストがある場合IShuffleFiveThings、そのリストにあるものは次のようになります。ボクシングやリフレクションを使用せずに、あらゆる種類のもの(クラス型と値型の両方を含む)を操作するために効率的に使用されます。対照的に、デリゲートを使用する場合は次のようになります。

void ActOn5RefParameters(ref p1、ref p2、ref p3、ref p4、ref p5);を委任します。

次に、特定のデリゲートインスタンスは、作成時に指定された単一のパラメータタイプにのみ作用できるため(リフレクションを介してのみ呼び出されるオープンデリゲートでない限り)、必要なオブジェクトのタイプごとにデリゲートの個別のリストを作成する必要があります。シャッフル(はい、通常は整数インデックスの配列を使用して順列を処理することを知っています。順列を操作として選択したのは、すべてのオブジェクトタイプに適用できるためであり、この特定の順列方法が役立つためではありません)。

タイプインには制約がないため、実装は型キャスト(ボクシングを導入する可能性があります)を除いて、タイプTインで多くのことを実行できないことに注意してください。IShuffleFiveThingsこのようなパラメータに制約を追加すると、それらがはるかに便利になります。インターフェイス内でそのような制約をハードコーディングすることは可能ですが、それはインターフェイスの有用性をそれらの特定の制約を必要とするアプリケーションに制限します。制約自体を一般的なものにすることで、その制限を回避できます。

于 2012-05-04T19:56:03.237 に答える
4

本質的には、そのデリゲートのインスタンスを作成しています。インスタンスには、ジェネリック型を定義する必要があります。

デリゲートの定義にはTを含めることができますが、インスタンスはどのTを定義する必要があります。

于 2012-05-04T19:46:14.460 に答える
3

この例を考えると:

public delegate void FooDelegate<T>(T value);

public class FooContainer
{
    public event FooDelegate<T> FooEvent;
}

あなたの例のようなコンパイラは、定義されていないFooEventため、宣言を好みません。TただしFooContainer

public delegate void FooDelegate<T>(T value);

public class FooContainer<T>
{
    public event FooDelegate<T> FooEvent;
}

そして今、コンパイラはこれで大丈夫です。なぜなら、FooContainerのインスタンスを作成する人は誰でも、そのようにタイプTを指定する必要があるからです。

FooContainer<string> fooContainer = new FooFooContainer<string>();

Tただし、このようなインターフェイスに制限することもできます。

public delegate void FooDelegate<T>(T value) where T : IFooValue;

public class FooContainer
{
    public event FooDelegate<IFooValue> FooEvent;

    protected void OnFooEvent(IFooValue value)
    {
        if (this.FooEvent != null)
            this.FooEvent(value);
    }
}

public interface IFooValue
{
    string Name { get; set; }// just an example member
}

この場合、インターフェースを実装している限り、型を使用してイベントを発生させることができますIFooValue

于 2012-05-04T20:19:23.670 に答える