Why doesn't C # output my generic types?

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(); // Does not compile :-( p.Process(query); // Must explicitly write all arguments p.Process<SomeQuery, string>(query); } } 

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.

+54
c # type-inference
Dec 14 '11 at 20:17
source share
5 answers

A bunch of people pointed out that C # doesn't draw conclusions based on restrictions. This is correct and relevant to the question. The conclusions are drawn by studying the arguments and their corresponding formal parameter types, and this is the only source of information about the conclusions.

After that, a bunch is associated with this article:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

This article is outdated and not relevant. It is deprecated because it describes the design decision we made in C # 3.0, which was then canceled in C # 4.0, mainly based on the response to this article. I just added an update to this article.

This does not matter, because the article is about returning the type of output from the arguments of a group of methods to standard delegate formats. This is not the situation the original poster asks for.

The relevant article I'm reading is most likely this:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

+43
Dec 14 2018-11-12T00:
source share

C # will not call generic types based on the return type of the generic method, only the arguments to the method.

It also does not use restrictions as part of type inference, which removes the general restriction of giving you a type.

For more information, see Eric Lippert's related post .

+14
Dec 14 2018-11-12T00:
source share

It does not use restrictions for type inference. Rather, it introduces types (when possible) and then checks for constraints.

Therefore, although the only possible TResult that can be used with the SomeQuery parameter, it will not see this.

Note also that it would be possible that SomeQuery also implement IQuery<int> , which is one of the reasons this compiler restriction might be a bad idea.

+9
Dec 14 '11 at 20:25
source share

The specification clearly describes this:

Section 7.4.2. Type inference

If the provided number of arguments differs from the number of parameters in the method, then the output is not immediately executed. Otherwise, suppose the general method has the following signature:

Tr M (T1 x1 ... Tm xm)

When a method of the form M (E1 ... Em) is called, the type inference task finds unique arguments of type S1 ... Sn for each of the parameters of type X1 ... Xn, so the call to M (E1 ... Em) becomes valid.

As you can see, the return type is not used for type inference. If the method call is not directly mapped to type arguments, then output is immediately generated.

The compiler does not just assume that you wanted string as an argument to TResult , and you could not. Imagine a TResult derived from a string. Both will be valid, so choose? Better to be explicit.

+4
Dec 14 '11 at 20:25
source share

Why they answered well, but there is an alternative solution. I encounter the same problems regularly, however dynamic or any solution using reflection or data distribution does not matter in my case (the joy of video games ...)

So instead, I pass the result as parameters to out which is then correctly output.

 interface IQueryProcessor { void Process<TQuery, TResult>(TQuery query, out TResult result) where TQuery : IQuery<TResult>; } class Test { void Test(IQueryProcessor p) { var query = new SomeQuery(); // Instead of // string result = p.Process<SomeQuery, string>(query); // You write string result; p.Process(query, out result); } } 

The only drawback I can think of is that it prohibits the use of "var".

0
Dec 05 '18 at 11:44
source share



All Articles