7

给定以下界面:

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

我可以使用什么 IEnumerable 类型来存储IEventHandler<TEvent>TEvent 不同的实现集合?

即给定以下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扩展方法并保留您的列表,假设事件的类型在编译时是已知的;仍然会有演员表,但你不会这样做,你只会得到可以实际处理该事件的条目。

于 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);

也许更好的方法是为每种事件类型设置一个列表

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 回答