У меня есть массив с типом EventHandler<EventBase>[]. В массиве есть объекты класса EventHandler<T>, где T - может быть любым классом, который наследуется от EventBase. Есть условие, что тип обобщения в массиве не повторяется
Мне нужно написать функцию, которая достает элемент из массива, тип обобщения которого задан типом обобщения самой функции. Например, мне надо найти объект с типом EventHandler<DamageEvent> (DamageEvent наследуется от EventBase). Для этого мне надо вот так вызвать эту функцию GetHandler<DamageEvent>()
Я бы это сделал и сам, но вот только не знаю, как узнать тип обобщения у объекта
Тип аргумента обобщенного класса можно узнать с помощью метода GetGenericArguments класса Type:
List<int> x = new List<int>();
var a = x.GetType().GetGenericArguments()[0];
Console.WriteLine(a.Name);
Он возвращает массив аргументов обобщения, а, так как, у вас всего один аргумент - просто возьмите из этого массива элемент с индексом 0.
Таким образом, ваш метод будет выглядеть как-то так:
EventHandler<EventBase>[] eventHandlers = new EventHandler<EventBase>[...];
EventHandler<EventBase> GetHandler<T>()
{
return eventHandlers.Single(eh => eh.GetType().GetGenericArguments()[0] == typeof(T));
}
На самом деле ваша задача не имеет никакого отношения к Generics или проверкой Generic Type Arguments.
Вам нужно в массиве объектов найти экземпляры определенного типа. С этим хорошо справляется стандартный Enumerable.OfType():
EventHandler<T> GetHandler<T>()
{
return eventHandlers.OfType<EventHandler<T>>().Single();
}
Никакие динамики или явный reflection не нужны.
И, на всякий случай - убедитесь, что в коллекции действительно лежат экземпляры нужного типа. Не уверен, что у вас происходит в коде, но со стандартным System.EventHandler<T> вы просто не сможете положить в коллекцию EventHandler<EventBase>[] экземпляр EventHandler<DamageEvent>, т.к. EventHandler от EventHandler<EventBase> не унаследован.
Т.е. код ниже не скопмилируется:
EventHandler<EventBase>[] eventHandlers = new EventHandler<EventBase>[2];
eventHandlers[0] = SomeHandler1;
eventHandlers[1] = new EventHandler<DamageEvent>(SomeHandler2);
и вам придется хранить хэндлеры как object[]
Если скорость не важна, то можно выполнить фокус с dynamic.
Например, есть функция
public void ProcEvent<T>(T[] events)
{
foreach(var e in events)
ProcEvent((dynamic)e);
}
И 3 перегрузки вызываемой функции, своя для каждого типа
public void ProcEvent(object @event)
{
Console.WriteLine("Object called");
}
public void ProcEvent(EventHandler<DamageEvent> @event)
{
Console.WriteLine("DamageEvent called");
}
public void ProcEvent(EventHandler<EventBase> @event)
{
Console.WriteLine("EventBase called");
}
Работу можно проверить так:
var events = new object[] {
new EventHandler<EventBase>((s, e) => {}) ,
new EventHandler<EventArgs>((s, e) => {}) ,
new EventHandler<DamageEvent>((s, e) => {}) ,
};
ProcEvent(events);
Вывод ожидаемо такой
EventBase called
Object called
DamageEvent called
Плюсы: код короче
Минусы: код медленный, ибо рефлексия. Код чуточку запутанный, ибо рефлексия :)
Сборка персонального компьютера от Artline: умный выбор для современных пользователей