不同类型的集合

时间:2012-11-19 15:18:55

标签: c# generics

给出以下界面:

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

我可以使用哪种IEnumerable类型来存储TEvent不同的IEventHandler<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(),
                           };
BTW,已经看到一些其他的答案主张使用基类型或继承的非通用接口,但看不出在这种情况下如何增加大量的价值,除非我遗漏了什么。是的,它允许我以一种稍微更安全的方式将它们全部添加到集合中,使用对象,但不会让我迭代它们并调用强类型的Process方法而不像我需要对象那样进行转换。 / p>
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);
}

有关如何改善这一点的想法吗?

3 个答案:

答案 0 :(得分:3)

我认为这里最好的方法是使用OfType扩展方法并保留你的List,假设在编译时知道事件的类型;仍然会有一个演员阵容,但你不会这样做,你只会获得可以实际处理该事件的条目。

答案 1 :(得分: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>();

答案 2 :(得分: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());
}