7

I'm building a generic query dispatcher. The idea is as follows:

  • A generic interface IQuery<T> that represents a query object
  • A concrete query class has some getters and setters (implementing IQuery<T>)
  • A generic interface IQueryHandler<TReturnType, TQuery> with a single method that receives a TQuery (with a constraint on TQuery: IQuery<TReturnType>) and returns a TReturnType
  • Concrete handlers implementing IQueryHandler that receive a concrete ConcreteQuery and return a TQuery
  • A QueryDispatcher that has a single method that receives a Query and returns a result. The concrete class will look up the correct handler from a DI container.

The problem is that I don't get type inference when I call the Fetch method on the QueryDispatcher. Is there anything I can do to get type inference or is this just a limitation of c#?

In theory it should know what the type arguments are since it receives a parameter of TQuery which has a constraint on being an IQuery<TReturnType.

This is the code:

class ConcreteClass    // This is the class of which I want to query objects
{
}

Query interface + concrete query class

interface IQuery<T>
{
}

public class ConcreteQuery : IQuery<ConcreteClass>
{
}

QueryHandler interface + concrete Query handler

interface IQueryHandler<TReturnType, TQuery> where TQuery : IQuery<TReturnType>
{
    TReturnType Fetch(TQuery query);
}

class ConcreteQueryHandler : IQueryHandler<ConcreteClass, ConcreteQuery>
{
    ConcreteClass Fetch(ConcreteQuery query)
    {
    }
}

QueryDispatcher (uses a DI container to resolve the correct handler)

class QueryDispatcher
{
    TReturnType Fetch<TReturnType, TQuery>(TQuery query) 
        where TQuery : IQuery<TReturnType>
    {
        return myDIcontainer.Get<IQueryHandler<T, TQuery>>().Fetch(query);
    }
}

Now when I use the QueryDispatcher like this I get an error:

var queryDispatcher = new QueryDispatcher();
var c = queryDispatcher.Fetch(new ConcreteQuery());

When I provide the type arguments everything works correctly:

var c = queryDispatcher.Fetch<ConcreteClass, ConcreteQuery>(new ConcreteQuery());
Kenneth
  • 28,294
  • 6
  • 61
  • 84

1 Answers1

1

What you want is a two level type inference and the compiler won't do it.

You might want to rethink your query dispatcher. Think of it as something that takes an IQuery<TReturnType> and returns a TReturnType.

Try changing your query dispatcher to:

class QueryDispatcher
{
    public TReturnType Fetch<TReturnType>(IQuery<TReturnType> query) 
    {
        return myDIcontainer
            .Get<IQueryHandler<TReturnType, IQuery<TReturnType>>>()
            .Fetch(query);
    }
}
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
  • But I'm right that in theory, the compiler should be able to do it, but that the C#-compiler just isn't smart enough, right? The problem with changing the query dispatcher like that is that I then can't resolve different query handlers for different IQuery's – Kenneth Feb 25 '14 at 17:27