I'm trying to setup a CoR with interfaces where a handler in the chain can be a for a less derived event type using contravariance. I create this interface to do it.
public interface IHandler<in TEvent>
{
void SetNext(IHandler<TEvent> next);
void Handle(TEvent event);
}
It fails with the compiler error
CS1961 Invalid variance: The type parameter 'TEvent' must be covariantly valid on 'IHandler.SetNext(IHandler)'. 'TEvent' is contravariant.
I thought this should be valid since IHandler
is covariant in TEvent
. What am I missing?
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.