some confusion with generic types in c#
This is a covariance problem. The real type of handler
is QueryHandler
, so it is a IQueryHandler<Query, bool>
. Of course Query
is an IQuery<bool>
, but that is the point of covariance.
It is like trying to assign a List<String>
to a variable of type List<Object>
.
There exists an out
keyword that allows you to use the covariance on your IQueryHandler
interface as you expect it.
See out for detailed information
EDIT:
As pointed out by Sweeper, you cannot use out
on TQuery
because it is used as input parameter. The correct solution is to avoid the dependecy of QueryHandler
on Query
. Isma has shown nicely how it is done.
This code does not work because IQueryHandler
is invariant on the TQuery
generic parameter. TQuery
needs to be covariant in order for handler
to be convertible to IQueryHandler<IQuery<T>, T>
, but that is impossible, as I will explain later. You could however, make TQuery
contravariant, which allows you to convert handler
to IQueryHandler<ASubclassOfQuery, T>
. TResult
can be covariant though. This is the code to do this:
public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>
See this page for more info about generic variances.
As for why handler
is not IQueryHandler<IQuery<T>, T>
, let's first suppose that it is, which means this code would compile:
IQueryHandler<IQuery<T>, T> q = handler;
q.Handle(new MyQuery<T>());
where MyQuery
is defined like this:
class MyQuery<T> : IQuery<T> {}
However, handler
is of runtime type QueryHandler
. QueryHandler.Handle
only handles Query
objects, not MyQuery<T>
objects! We have a contradiction, and hence our assumption that handler
is a IQueryHandler<IQuery<T>, T>
must be false.