У меня есть массив с типом 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
Плюсы: код короче
Минусы: код медленный, ибо рефлексия. Код чуточку запутанный, ибо рефлексия :)
На данный вопрос уже ответили:
Имею простое приложение c# Windows formДалее есть единственная форма с контролом richtextbox, который закреплен на всей форме(по размеру)