Я разрабатывал для себя различные системы событий для игр, в которых слушатель получал общий объект типа события и должен был отличать его реальный тип с помощью переключателя или аналогичного, а затем приводить к правильному подкласс.Контравариантные проблемы с делегатами 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 и так далее.
Заранее благодарим!
Спасибо, Томас! Наличие делегата типа в словаре позволяет делегатам быть общим и решает эту часть. Никогда не стесняйтесь. Я также заметил, что класс Delegate может вызвать DynamicInvoke без необходимости кастинга. Есть ли разница, например, производительность? Поскольку установка общих методов Dispatch позволяет ограничить, например, очередность всех событий перед их отправкой. –