It makes a lot of sense if you think in terms of Animal
s and Dog
s.
Let's make up some concrete implementations first:
class AnimalHandler: IHandler<Animal> {
public void SetNext(IHandler<Animal> next) {
// animal handlers can handle cats :)
next.Handle(new Cat());
}
// ...
}
class DogHandler: IHandler<Dog> { ... }
Since the type parameter of IHandler
is contravariant, a IHandler<Animal>
is a kind of IHandler<Dog>
:
IHandler<Animal> someAnimalHandler = new AnimalHandler();
IHandler<Dog> dogHandler = someAnimalHandler;
Suppose that your SetNext
method is allowed to be declared in IHandler
, then I can call SetNext
with another IHandler<Dog>
on this dogHandler
, since that is exactly the type that dogHandler.SetNext
accepts.
IHandler<Dog> anotherDogHandler = new DogHandler();
dogHandler.SetNext(anotherDogHandler);
But that shouldn't be possible! After all, dogHandler
actually stores an instance of AnimalHandler
, whose SetNext
method accepts IHandler<Animal>
, and a IHandler<Dog>
is not necessarily a IHandler<Animal>
, is it? Imagine what would happen if the implementation of AnimalHandler.SetNext
actually runs - keep in mind that the next
parameter is a DogHandler
- we would be asking a dog handler to handle a cat!
Therefore, it is invalid to declare SetNext
with a contravariant TEvent
.
This is specified more precisely here. It is said that the type IHandler<TEvent>
is input-unsafe, and so is prohibited as a parameter.
TEvent
would have to be invariant for both of your interface methods to compile.