2015-10-17 30 views
1

我一直在开发一些游戏的不同事件系统,其中侦听器接收到通用事件类型对象,并且必须将其真实类型与开关或类似对象进行区分,然后将其转换为正确的子类事件。字典上的C#代表的逆变问题

不同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等等。
在此先感谢!

回答

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方法来投,但在这一点上你知道演员是正确的。

+0

谢谢,托马斯!在字典中使用类型委托允许代表是通用的并解决该部分。从来没有遇到过。我也注意到Delegate类可以调用DynamicInvoke而不需要强制转换。有没有什么区别,比如表演等等?因为设置Dispatch方法通用限制了可能性,例如在分派所有事件之前排队所有事件。 –