C#代表在字典

时间:2015-10-17 11:39:45

标签: c# delegates event-handling covariance contravariance

我一直在开发一些不同的游戏事件系统,其中侦听器接收到一般事件类型对象,并且必须将其真实类型与开关或类似物区分开,然后转换为正确的子类事件。

在不同的aproaches之后,我能够使用如下结构(简化)摆脱开关盒:

public class Event {}
public class EventA : Event {}
public class EventB : Event {}

public delegate void HanlderDelegate(Event ev);

public class EventManager
{
    Dictionary<Type, HanlderDelegate> delegateMap = new Dictionary<Type, HanlderDelegate>();

    public void Dispatch(Event ev)
    {
        if (delegateMap.ContainsKey(ev.GetType()))
        {
            delegateMap[ev.GetType()].Invoke(ev);
        }
    }

    public void Register<T>(HanlderDelegate handler) where T : Event
    {
        delegateMap.Add(typeof(T), handler);
    }
}

public class EventListener
{
    public EventListener(EventManager evtMgr)
    {
        evtMgr.Register<EventA>(this.HandleEventA);
        evtMgr.Register<EventB>(this.HandleEventB);
    }

    public void HandleEventA(Event ev)
    {
        EventA evA = (EventA)ev;
        //... do stuff
    }

    public void HandleEventB(Event ev)
    {
        EventB evB = (EventB)ev;
        //... do stuff
    }
}

我对这种方法很满意,但我仍然发现每种方法中的转换都是可以改进的。我试图让委托更通用

public delegate void HanlderDelegate<T>(T ev) where T : Event;因此,听众可以直接实施public void HandleEvent(EventA ev)public void HandleEvent(EventB ev)并注册。
但是,当然,EventManager类中的字典应存储指向HanlderDelegate<Event>的指针,并且存在问题开始的地方,我无法将HanlderDelegate<EventA>强制转换为HanlderDelegate<Event>以便存储它们,并且同时存在时间以另一种方式来调用它们。

有没有办法实现这个目标?我知道编译器会允许奇怪的东西,但我知道它并且可以通过代码控制EventB没有错误地被转换到EventA等等。
提前谢谢!

1 个答案:

答案 0 :(得分:0)

您可以将委托和Dispatch方法设为通用,并将处理程序存储为Delegate而不是HandlerDelegate<T>

delegate void HandlerDelegate<TEvent>(TEvent ev) where TEvent : Event;

class EventManager
{
    Dictionary<Type, Delegate> delegateMap = new Dictionary<Type, Delegate>();

    public void Dispatch<TEvent>(TEvent ev) where TEvent : Event
    {
        Delegate d;
        if (delegateMap.TryGetValue(typeof(TEvent), out d))
        {
            var handler = (HandlerDelegate<TEvent>)d;
            handler(ev);
        }
    }

    public void Register<TEvent>(HandlerDelegate<TEvent> handler) where TEvent : Event
    {
        delegateMap.Add(typeof(TEvent), handler);
    }
}

当然,您仍然必须使用Dispatch方法,但此时您知道演员阵容是正确的。