2015-10-17 2 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 позволяет ограничить, например, очередность всех событий перед их отправкой. –

 Смежные вопросы

  • Нет связанных вопросов^_^