5

Possible Duplicate:
No type inference with generic extension method

Consider two methods:

public static IEnumerable<V> Merge<V>
       (this IEnumerable<IEnumerable<V>> coll)

public static IEnumerable<V> Merge<T, V>
       (this IEnumerable<T> coll) 
              where T : IEnumerable<V>

Both compile just fine, in both cases the type of generic types will be known at compile time of caller, and thus the exact type of extended type.

You can call both fine, but only the first one as extension.

Why?

Update 1

To see it fail, use the second method and such example:

    var x = new List<List<int>>();
    var y = x.Merge();

Update -- closing

Don't you guys think the original post is WAY too elaborate to get the clear picture? For educational purposes I think this post shouldn't be closed, even if technically (i.e. the answer) it is duplicate. Just my 2 cents.

Community
  • 1
  • 1
greenoldman
  • 16,895
  • 26
  • 119
  • 185
  • Ah i see, so when you say using the second method, you're talking if the 2nd is by itself. Not that both of these are overloads of each other... Right? – James Michael Hare Aug 24 '11 at 19:09
  • 1
    In that case, the SO answer Eric referred you to is the actual reason. Because the relationship between T and V is specified in the constraint, and the compiler will not infer types from constraints, the compiler has no idea how to resolve your generic type parameter V, which is why you must specify type argument V explicitly. – James Michael Hare Aug 24 '11 at 19:20
  • @James Michael Hare, somehow I missed the part that template arguments have to be resolved, not only method ;-) Now it is quite easy to understand compiler (however it would help, if constraint could be part of the method definition). – greenoldman Aug 24 '11 at 19:54

2 Answers2

4

Method type inference does not take constraints into account when making inferences.

This same question was asked yesterday. See my answer there for more details.

No type inference with generic extension method

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Either I miss something, or your shorter answer is not sufficient here "deductions are made from the arguments and the formal parameters". It should be possible (2nd case) to get the type of the argument, it is T, which could be (like in James' example) List. So T is know, extension should work. Or shouldn't? ;-) I need time to let it sink in... – greenoldman Aug 24 '11 at 17:48
  • 1
    @macias: The facts you have are (1) T is List, and (2) T is constrained to be IEnumerable. We do not make deductions from constraints. Therefore fact (2) is never considered, and therefore we have nothing from which to deduce V. – Eric Lippert Aug 24 '11 at 18:33
  • Ah, now I get it. If you still reading my comment, please clarify your blog entry, that you meant template argument, not the method argument. I don't know why, but when reading your answers and blog and seeing "argument" I constantly related it to method argument, and not template argument (as I should). Now it is perfect clear! Thank you. – greenoldman Aug 24 '11 at 19:52
  • @macias: You're welcome! I agree, the terminology is confusing. We have "arguments" and "formal parameters", and then analogously "type arguments" and "type parameters". It is easy to accidentally use one term when you mean another, and can be quite confusing. – Eric Lippert Aug 24 '11 at 19:57
2

I don't think the problem is that the second CAN'T be called, but that IntelliSense won't see it because it can't easily infer the second generic type parameter V from your call without explicit help.

For example, given your two extension methods, the following are all legal

    // IEnumerable<IEnumerable<int>> definition...
    List<List<int>> x = ...;

    // calls your first method (implicitly)
    x.Merge();

    // also calls your first method (explicitly)
    x.Merge<int>();

    // calls your second method (explicitly)
    x.Merge<List<int>, int>();

All three of these compile successfully, I just think with the two generic type parameters, it can't infer your second generic type parameter from usage and thus it's not showing in intellisense, but still legal...

UPDATE: As per the asker, it's not that the two methods were declared as overloads, but that they were either/or. Given that reason Merge() doesn't work on the 2nd form because the relationship between T and V are defined in the type constraints, and thus are not used for type inference as Eric stated in his S.O. answer.

James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • I meant compiler, not IntelliSense. It is sufficient for me to write first method and use it, and then exchange this method for the second one. In first case code compiles OK, in second -- not. Btw. you forgot about fourth case -- x.Merge() for the second one. It should (I wished...) compile too. – greenoldman Aug 24 '11 at 17:23
  • @macias: Can you give an example of the call that would not compile? When you say it doesn't compile, are you saying with inference? Or without? – James Michael Hare Aug 24 '11 at 17:37
  • I updated the question with short example. – greenoldman Aug 24 '11 at 17:58