Is the C # compiler unable to infer method type parameters from the expected return type?

This seems strange to me, but I remember the thread in which Eric Lippert commented on the impossibility (by design or at least the convention, I think) of C # to overload methods based on return type, so maybe this is in some confusing way related to this.

Is there a reason this doesn't work:

public static T Test<T>() where T : new() { return new T(); } // Elsewhere SomeObject myObj = Test(); 

But it does:

  var myObj = Test<SomeObject>(); 

From a certain point of view, they are both good, in that you do not repeat yourself (in a very small way), but is this just a different compiler pass?

+6
c # type-inference
source share
5 answers

Check out the C # language specification. ยง7.5.2, the type of a variable declaration is not a certification for type inference, and obviously this should not be. Consider the following code:

 Base b = Test<Derived>(); Derived d = Test<Derived>(); 

The type of the return method is probably different from the type of the variable declaration, since we have an implicit conversion in C #.

+3
source share

First of all, this is a "return type based overload":

 void M(int x){...} int M(int x){...} string M(int x){...} 

Declarations are not legal; you cannot overload a method based on the type of the return type, because the return type is not part of the signature, and the signature must be unique.

What you are talking about is method type inference based on the type of the returned method. We also do not support this.

The reason is that the type of return may be what you are trying to figure out.

 M(Test()); 

What is the type of test return? It depends on which overload M we choose. What overload M will we choose? It depends on the type of test returned.

In general, C # is designed in such a way that each subexpression has a type, and types are developed from "inside" to "outside", and not from outside to inside.

Notable exceptions are anonymous functions, method groups, and null:

 M(x=>x+1) 

What type is x => x + 1? It depends on which overload M is caused.

 M(N); // N is a method group 

what type of n? Again, it depends on which overload M is caused.

And so on. In these cases, we reason from "outside" to "inside."

The output type involving lambda is extremely complex and difficult to implement. We do not want to have the same complication and difficulties throughout the compiler.

+5
source share

With the exception of unmatched expressions ( null , method groups, and lambda expressions), the type of expression must be statically determined by the expression itself, regardless of context.

In other words, the type of the Test() expression cannot depend on what you assign it to.

+4
source share

Type inference by the compiler does not use the "expected type" of the destination as part of the logic.

So, the "scope of consideration" for type inference is not this:

 SomeObject myObj = Test(); 

but this:

 Test(); 

And there are no clues regarding the expected type.

+1
source share

If you need an example of why the type of an expression should be defined by the expression itself, consider the following two cases:

  • We do not use the return value at all - we just call the method for its side effects.
  • We pass the return value directly to the overloaded method

Using the "expected type" of the return value when it comes to resolving a general type will put all the complexity into the compiler, and all you get is that sometimes you need to explicitly specify the type, and sometimes you donโ€™t, and whether you or not, may change based on unrelated changes elsewhere in the code.

+1
source share

All Articles