Method Inference does not work with method group
Asked Answered
W

3

12

Consider

void Main()
{
    var list = new[] {"1", "2", "3"};
    list.Sum(GetValue); //error CS0121
    list.Sum(s => GetValue(s)); //works !
}

double GetValue(string s)
{
    double val;
    double.TryParse(s, out val);
    return val;
}

The description for the CS0121 error is

The call is ambiguous between the following methods or properties: 'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal>)' and 'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal?>)'

What I don't understand is, what information does s => GetValue(s) give the compiler that simply GetValue doesn't - isn't the latter syntactic sugar for the former ?

Wishywashy answered 12/10, 2011 at 20:3 Comment(0)
D
19

Mark's answer is correct but could use a bit more explanation.

The problem is indeed due to a subtle difference between how method groups are handled and how lambdas are handled.

Specifically, the subtle difference is that a method group is considered to be convertible to a delegate type solely on the basis of whether the arguments match, not also on the basis of whether the return type matches. Lambdas check both the arguments and the return type.

The reason for this odd rule is that method group conversions to delegates are essentially a solution of the overload resolution problem. Suppose D is the delegate type double D(string s) and M is a method group containing a method that takes a string and returns a string. When resolving the meaning of a conversion from M to D, we do overload resolution as if you had said M(string). Overload resolution would pick the M that takes a string and returns a string, and so M is convertible to that delegate type even though the conversion will result in an error later. Just as "regular" overload resolution would succeed if you said "string s = M(null);" -- overload resolution succeeds, even though that causes a conversion failure later.

This rule is subtle and a bit weird. The upshot of it here is that your method group is convertible to all the different delegate types that are the second arguments of every version of Sum that takes a delegate. Since no best conversion can be found, the overload resolution on method group Sum is ambiguous.

Method group conversions rules are plausible but a bit odd in C#. I am somewhat vexed that they are not consistent with the more "intuitively correct" lambda conversions.

Dennadennard answered 13/10, 2011 at 2:35 Comment(3)
If you had a time machine, how would you change them? Would you make them consider the return types, like lambdas? Or would you make a different subtle change?Khorma
Interesting! I played around with it in LinqPad and indeed, even if I have double D1(string s) and int D1(string s), a method group with a signature of string M(string) would produce an ambiguous call error - even though both signatures would not fit anyway. This as opposed to a lambda, that would try them all and give you a conversion error on the last (first?) one it tries.Wishywashy
However, if there is no overload resolution problem (and the wrong signature is used), the method group error ("M has the wrong return type") is slightly more informative than the lambda's ("Cannot implicitly convert type 'string' to 'double'")Wishywashy
L
9

s => GetValue(s) is a lambda expression and GetValue is a method group, which is a completely different thing. They can both be considered syntactic sugar for new Func<string,double>(...) but the only way they are related to each other is that the lambda expression includes a call to GetValue(). When it comes to converting to a delegate, method groups have different conversion rules than lambda expressions with respect to return types. See Why is Func<T> ambiguous with Func<IEnumerable<T>>? and Overloaded method-group argument confuses overload resolution?.

Lilah answered 12/10, 2011 at 20:14 Comment(0)
B
3

Note that as of latest compiler versions this works (@sharplab, @dotnetfiddle with .NET Core 3.1). For those who are not faint of heart you can do a deep dive into the Method group conversions part of the specification to see what exactly has changed that has lead to such effect.

Biscay answered 18/12, 2023 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.