I have a lot of fun funcy (fun destinations) using common methods. In most cases, C # type inference is smart enough to figure out what common arguments it should use for my common methods, but now I have a project where the C # compiler does not succeed, although I believe that it can find the right types.
Can someone tell me if the compiler is a little stupid in this case, or is there a very clear reason why it cannot output my general arguments?
Here is the code:
Class and interface definitions:
interface IQuery<TResult> { } interface IQueryProcessor { TResult Process<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>; } class SomeQuery : IQuery<string> { }
Invalid code that does not compile:
class Test { void Test(IQueryProcessor p) { var query = new SomeQuery();
Why is this? What am I missing here?
Here's the compiler error message (this leaves us nothing good):
Type arguments for the IQueryProcessor.Process (TQuery) method cannot be taken out of use. Try specifying enter the arguments explicitly.
I believe that C # should be able to conclude that this is due to the fact that:
- I provide an object that implements
IQuery<TResult> . - Only the version of
IQuery<TResult> that implements the type has an IQuery<string> value, and therefore TResult must be a string . - With this information, the compiler has TResult and TQuery.
Decision
For me, the best solution was to change the IQueryProcessor interface and use dynamic typing in the implementation:
public interface IQueryProcessor { TResult Process<TResult>(IQuery<TResult> query); } // Implementation sealed class QueryProcessor : IQueryProcessor { private readonly Container container; public QueryProcessor(Container container) { this.container = container; } public TResult Process<TResult>(IQuery<TResult> query) { var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult)); dynamic handler = container.GetInstance(handlerType); return handler.Handle((dynamic)query); } }
The IQueryProcessor interface now accepts the IQuery<TResult> parameter. Thus, he can return TResult , and this will solve the problems from the point of view of the consumer. We need to use reflection in the implementation to get the actual implementation, since we need specific types of requests (in my case). But here comes a dynamic seal of help that will reflect us. You can read more about this in the article.