Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

some confusion with generic types in c#

I'm playing with cqs a little bit and I'm trying to implement this in a class library (so there's no IOC, IServiceProvider, etc). Here is some code that I wrote:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

public class Query : IQuery<bool>
{
    public int Value { get; set; }
}

public class QueryHandler : IQueryHandler<Query, bool>
{
    public bool Handle(Query query)
    {
        return query.Value > 0;
    }
}

public class Dispatcher
{
    private readonly Dictionary<Type, object> handlers = new Dictionary<Type, object>();

    public Dispatcher()
    {
        handlers.Add(typeof(Query), new QueryHandler());
    }

    public T Dispatch<T>(IQuery<T> query)
    {
        IQueryHandler<IQuery<T>, T> queryHandler;

        if (!this.handlers.TryGetValue(query.GetType(), out object handler) ||
            ((queryHandler = handler as IQueryHandler<IQuery<T>, T>) == null))
        {
            throw new Exception();
        }

        return queryHandler.Handle(query);
    }
}

And this si how I am calling my code:

Query query = new Query();
Dispatcher dispatcher = new Dispatcher();
var result = dispatcher.Dispatch(query);

But the problem is that inside the dispatcher, I don't know why the variable handler cannot be casted as IQueryHandler<IQuery<T>,T>. Here is some extra data: enter image description here

PS: I know how to make this work(with dynamic), but I want to understand why THIS code isn't working.

like image 686
Buda Gavril Avatar asked Sep 13 '19 06:09

Buda Gavril


1 Answers

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.

like image 136
Eric Pitz Avatar answered Sep 19 '22 11:09

Eric Pitz