Вам нужно, чтобы IManager
был ковариантным.
interface IManager<out TEvent> where TEvent : IEvent
Таким образом, это назначение становится легальным:
IManager<IEvent> manager = new Manager<IMoreSpecificEvent>();
Ковариация, в самом простом смысле, указывает, что вам все равно, является ли параметр типа более производным, чем ограничение для такого назначения. Это означает, что, помимо прочего, нигде в ковариантной реализации (так в вашем менеджере
Вы можете прочитать больше о ковариации в C # здесь .
EDIT:
Правильно, вот этот метод:
void Foo(TEvent mySpecificEvent);
разрушает наш маленький план здесь. Прямо сейчас ваш Manager
не является ковариантным, и на то есть веская причина. Давайте рассмотрим список:
List<IManager<IEvent>> list;
и скажите, что это назначение допустимо ( это не , но давайте представим):
IManager<ISpecificNumericEvent> manager = new SpecificNumericManager();
list.Add(manager);
Что произойдет, если мы передадим этот список, чтобы выполнить некоторые операции с менеджером? Ну, все, что мы знаем, это то, что внутри есть вещи типа IManager<IEvent>
. Это означает, что мы должны быть в состоянии передать его любой IEvent
в метод Foo
, верно?
class BaseEvent : IEvent {}
foreach(var manager in list)
{
IEvent event = new BaseEvent();
manager.Foo(event);
}
Но так как мы ранее присвоили SpecificNumericManager
списку, мы пытаемся вызвать метод с подписью:
void Foo(TEvent event) where TEvent : ISpecificNumericEvent
с BaseEvent
, который не реализует ISpecificNumericEvent
. Бум, система типов разрушена в результате огненного взрыва, кошки и собаки живут вместе, массовая истерия.
Этот интерфейс не может быть ковариантным по причинам, указанным выше - C # просто запрещает обход системы типов ранее, запрещая ковариантное присвоение IManager<IEvent>
. По тем же причинам плохая идея будет подавать события цикла в универсальные IManager<IEvent>
, потому что просто нет идеи узнать, сможет ли конкретный менеджер обработать это конкретное событие во время компиляции. Вам нужно подумать о том, чего вы действительно пытаетесь достичь с помощью этого цикла. Обычно, когда правила ковариации / контравариантности нарушаются, это потому, что то, что вы хотите сделать, на самом деле не имеет смысла. Однако, если вы хотите сделать что-то сумасшедшее с типами в общем списке IManager<IEvent>
, то у вас не будет системы типов на вашей стороне, и вам придется использовать рефлексию для достижения ваших коварных целей.