7

次のインターフェースがあるとします。

public interface IEventHandler<in TEvent> where TEvent : IEvent
{
    void Process(TEvent @event);
}

IEventHandler<TEvent>TEvent が異なる実装のコレクションを格納するには、どの IEnumerable 型を使用できますか?

つまり、次の 3 つの実装があるとします。

public class BlahEvent1EventHandler : IEventHandler<Event1>
{
    ...
}

public class WhateverEvent1EventHandler : IEventHandler<Event1>
{
    ...
}

public class BlahEvent2EventHandler : IEventHandler<Event2>
{
    ...
}

オブジェクトのコレクション以上のことはできますか?

        var handlers = new List<object>
                           {
                               new BlahEvent1EventHandler(),
                               new WhateverEvent1EventHandler(),
                               new BlahEvent2EventHandler(),
                           };

ところで、基本型または継承された非ジェネリック インターフェイスの使用を提唱する他のいくつかの回答を見てきましたが、何かが欠けていない限り、この場合にそれがどのように大きな価値を追加するかわかりません。はい、オブジェクトを使用するよりも少し型安全な方法でそれらすべてをコレクションに追加できますが、それらを反復処理して、オブジェクトで行う必要があるのと同じように、キャストせずに厳密に型指定された Process メソッドを呼び出すことはできません。

public interface IEventHandler
{        
}

public interface IEventHandler<in TEvent> : IEventHandler where TEvent : IEvent
{
    void Process(TEvent @event);
}

IEnumerable<IEventHandler>または持っている場合は、まだキャストする必要がありますIEnumerable<obect>

foreach (var handler in _handlers.Cast<IEventHandler<TEvent>>())
{
    handler.Process(@event);
}

これを改善する方法について何か考えはありますか?

4

3 に答える 3

3

ここでの最善のアプローチは、イベントのタイプがコンパイル時にわかっていると仮定して、 OfType拡張メソッドを使用して List を保持することだと思います。キャストはまだありますが、それを行うことはなく、実際にそのイベントを処理できるエントリのみを取得します。

于 2012-11-19T17:04:51.080 に答える
1

おそらくあなたのデザインは最適ではありません。イベントハンドラーに実装するのではなく、イベントタイプ固有の動作を必要とするすべてのコードをイベントに移動してみてください。つまり、ポリモーフィズムがあなたのために働くようにします。

public interface IEventHandler
{
    void Process(IEvent evt);
}

例として、イベント固有のプロパティに応じてメッセージを作成する必要があると仮定します。イベントハンドラー内でメッセージを作成する代わりに、イベントでメッセージを作成します

public interface IEvent
{
    string Message { get; }
    ...
}

特定のイベント

public class TestEvent : IEvent
{
    public string A { get; set; } // Event specific property
    public string B { get; set; } // Event specific property

    public string Message { get { return String.Format("{0} {1}", A, B); } }
    // The event handler does not need to access A or B.
}

アップデート

あなたが意図した方法でリストを定義する方法があったと仮定しましょう

var handlers = new List<?>();

どのようにキャストしますか?

var handler = handlers[i];
// How to cast?
((?)handler).Process((?)evt);

たぶん、より良い方法は、イベントタイプごとに1つのリストを持つことです。

public static class EventHandler<in TEvent> : IEventHandler<TEvent>
    where TEvent : IEvent
{
    public static readonly List<IEventHandler<TEvent>> Handlers =
        new List<IEventHandler<TEvent>>();

    ...
}

次に、このようなイベントハンドラーにアクセスできます

SpecificEventType specEvent = ...;
EventHandler<SpecificEventType>.Handlers[0].Process(specEvent);

更新#2

まったく異なるソリューションは、弱い型のリストをカプセル化し、ジェネリックメソッドを使用して強い型のインターフェイスを提供する新しいコレクションクラスを作成します

public class HandlerCollection : IEnumerable
{
    private readonly List<object> _handlers = new List<object>();

    public void Add<TEvent>(IEventHandler<TEvent> handler)
        where TEvent : IEvent
    {
        _handlers.Add(handler);
    }

    public IEventHandler<TEvent> Find<TEvent>()
        where TEvent : IEvent
    {
        return _handlers
            .OfType<IEventHandler<TEvent>>()
            .FirstOrDefault();
    }

    public IEventHandler<TEvent> Find<TEvent>(Func<IEventHandler<TEvent>, bool> predicate)
        where TEvent : IEvent
    {
        return _handlers
            .OfType<IEventHandler<TEvent>>()
            .Where(predicate)
            .FirstOrDefault();
    }

    // Collection initializers can only be applied to types implementing IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        return _handlers.GetEnumerator();
    }
}

テスト

var handlers = new HandlerCollection {
                   new BlahEvent1EventHandler(),
                   new WhateverEvent1EventHandler(),
                   new BlahEvent2EventHandler()
               };
IEventHandler<WhateverEvent1> eh = handlers.Find<WhateverEvent1>();
于 2012-11-19T17:40:48.980 に答える
0

ハンドラーを介して列挙し、そのProcessインターフェースメンバーを呼び出す必要がある場合は、次のようにクラスを実装できます。

public class BlahEvent1EventHandler : IEventHandler<IEvent>
{
    ...
}

public class WhateverEvent1EventHandler : IEventHandler<IEvent>
{
    ...
}

public class BlahEvent2EventHandler : IEventHandler<IEvent>
{
    ...
}

public class Event1 : IEvent
{}

public class Event2 : IEvent
{}

次に、それらを使用します。

List<IEventHandler<IEvent>> list = new List<IEventHandler<IEvent>>();

list.Add(new BlahEvent1EventHandler());

foreach (IEventHandler<IEvent> eventHandler in list)
{
eventHandler.Process(new Event1());
}
于 2012-11-19T15:32:49.440 に答える