Method Inference does not work with method group - c#

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 ?

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.

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?.

Related

The call is ambiguous between Func<T> and Func<Task<T>> [duplicate]

The following call to the overloaded Enumerable.Select method:
var itemOnlyOneTuples = "test".Select<char, Tuple<char>>(Tuple.Create);
fails with an ambiguity error (namespaces removed for clarity):
The call is ambiguous between the following methods or properties:
'Enumerable.Select<char,Tuple<char>>
(IEnumerable<char>,Func<char,Tuple<char>>)'
and
'Enumerable.Select<char,Tuple<char>>
(IEnumerable<char>, Func<char,int,Tuple<char>>)'
I can certainly understand why not specifying the type-arguments explicitly would result in an ambiguity (both the overloads would apply), but I don't see one after doing so.
It appears clear enough to me that the intention is to call the first overload, with the method-group argument resolving to Tuple.Create<char>(char). The second overload should not apply because none of the Tuple.Create overloads can be converted to the expected Func<char,int,Tuple<char>> type. I'm guessing the compiler is confused by Tuple.Create<char, int>(char, int), but its return-type is wrong: it returns a two-tuple, and is hence not convertible to the relevant Func type.
By the way, any of the following makes the compiler happy:
Specifying a type-argument for the method-group argument: Tuple.Create<char> (Perhaps this is actually a type-inference issue?).
Making the argument a lambda-expression instead of a method-group: x => Tuple.Create(x). (Plays well with type-inference on the Select call).
Unsurprisingly, trying to call the other overload of Select in this manner also fails:
var itemIndexTwoTuples = "test".Select<char, Tuple<char, int>>(Tuple.Create);
What's the exact problem here?
First off, I note that this is a duplicate of:
Why is Func<T> ambiguous with Func<IEnumerable<T>>?
What's the exact problem here?
Thomas's guess is essentially correct. Here are the exact details.
Let's go through it a step at a time. We have an invocation:
"test".Select<char, Tuple<char>>(Tuple.Create);
Overload resolution must determine the meaning of the call to Select. There is no method "Select" on string or any base class of string, so this must be an extension method.
There are a number of possible extension methods for the candidate set because string is convertible to IEnumerable<char> and presumably there is a using System.Linq; in there somewhere. There are many extension methods that match the pattern "Select, generic arity two, takes an IEnumerable<char> as the first argument when constructed with the given method type arguments".
In particular, two of the candidates are:
Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,Tuple<char>>)
Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,int,Tuple<char>>)
Now, the first question we face is are the candidates applicable? That is, is there an implicit conversion from each supplied argument to the corresponding formal parameter type?
An excellent question. Clearly the first argument will be the "receiver", a string, and it will be implicitly convertible to IEnumerable<char>. The question now is whether the second argument, the method group "Tuple.Create", is implicitly convertible to formal parameter types Func<char,Tuple<char>>, and Func<char,int, Tuple<char>>.
When is a method group convertible to a given delegate type? A method group is convertible to a delegate type when overload resolution would have succeeded given arguments of the same types as the delegate's formal parameter types.
That is, M is convertible to Func<A, R> if overload resolution on a call of the form M(someA) would have succeeded, given an expression 'someA' of type 'A'.
Would overload resolution have succeeded on a call to Tuple.Create(someChar)? Yes; overload resolution would have chosen Tuple.Create<char>(char).
Would overload resolution have succeeded on a call to Tuple.Create(someChar, someInt)? Yes, overload resolution would have chosen Tuple.Create<char,int>(char, int).
Since in both cases overload resolution would have succeeded, the method group is convertible to both delegate types. The fact that the return type of one of the methods would not have matched the return type of the delegate is irrelevant; overload resolution does not succeed or fail based on return type analysis.
One might reasonably say that convertibility from method groups to delegate types ought to succeed or fail based on return type analysis, but that's not how the language is specified; the language is specified to use overload resolution as the test for method group conversion, and I think that's a reasonable choice.
Therefore we have two applicable candidates. Is there any way that we can decide which is better than the other? The spec states that the conversion to the more specific type is better; if you have
void M(string s) {}
void M(object o) {}
...
M(null);
then overload resolution chooses the string version because string is more specific than object. Is one of those delegate types more specific than the other? No. Neither is more specific than the other. (This is a simplification of the better-conversion rules; there are actually lots of tiebreakers, but none of them apply here.)
Therefore there is no basis to prefer one over the other.
Again, one could reasonably say that sure, there is a basis, namely, that one of those conversions would produce a delegate return type mismatch error and one of them would not. Again, though, the language is specified to reason about betterness by considering the relationships between the formal parameter types, and not about whether the conversion you've chosen will eventually result in an error.
Since there is no basis upon which to prefer one over the other, this is an ambiguity error.
It is easy to construct similar ambiguity errors. For example:
void M(Func<int, int> f){}
void M(Expression<Func<int, int>> ex) {}
...
M(x=>Q(++x));
That's ambiguous. Even though it is illegal to have a ++ inside an expression tree, the convertibility logic does not consider whether the body of a lambda has something inside it that would be illegal in an expression tree. The conversion logic just makes sure that the types check out, and they do. Given that, there's no reason to prefer one of the M's over the other, so this is an ambiguity.
You note that
"test".Select<char, Tuple<char>>(Tuple.Create<char>);
succeeds. You now know why. Overload resolution must determine if
Tuple.Create<char>(someChar)
or
Tuple.Create<char>(someChar, someInt)
would succeed. Since the first one does and the second one does not, the second candidate is inapplicable and eliminated, and is therefore not around to become ambiguous.
You also note that
"test".Select<char, Tuple<char>>(x=>Tuple.Create(x));
is unambiguous. Lambda conversions do take into account the compatibility of the returned expression's type with the target delegate's return type. It is unfortunate that method groups and lambda expressions use two subtly different algorithms for determining convertibility, but we're stuck with it now. Remember, method group conversions have been in the language a lot longer than lambda conversions; had they been added at the same time, I imagine that their rules would have been made consistent.
I'm guessing the compiler is confused by Tuple.Create<char, int>(char, int), but its return-type is wrong: it returns a two-tuple.
The return type isn't part of the method signature, so it isn't considered during overload resolution; it's only verified after an overload has been picked. So as far as the compiler knows, Tuple.Create<char, int>(char, int) is a valid candidate, and it is neither better nor worse than Tuple.Create<char>(char), so the compiler can't decide.

Why is Foo(Action bar) and Foo(Func<Task> bar) ambiguous when used with method groups? [duplicate]

The following call to the overloaded Enumerable.Select method:
var itemOnlyOneTuples = "test".Select<char, Tuple<char>>(Tuple.Create);
fails with an ambiguity error (namespaces removed for clarity):
The call is ambiguous between the following methods or properties:
'Enumerable.Select<char,Tuple<char>>
(IEnumerable<char>,Func<char,Tuple<char>>)'
and
'Enumerable.Select<char,Tuple<char>>
(IEnumerable<char>, Func<char,int,Tuple<char>>)'
I can certainly understand why not specifying the type-arguments explicitly would result in an ambiguity (both the overloads would apply), but I don't see one after doing so.
It appears clear enough to me that the intention is to call the first overload, with the method-group argument resolving to Tuple.Create<char>(char). The second overload should not apply because none of the Tuple.Create overloads can be converted to the expected Func<char,int,Tuple<char>> type. I'm guessing the compiler is confused by Tuple.Create<char, int>(char, int), but its return-type is wrong: it returns a two-tuple, and is hence not convertible to the relevant Func type.
By the way, any of the following makes the compiler happy:
Specifying a type-argument for the method-group argument: Tuple.Create<char> (Perhaps this is actually a type-inference issue?).
Making the argument a lambda-expression instead of a method-group: x => Tuple.Create(x). (Plays well with type-inference on the Select call).
Unsurprisingly, trying to call the other overload of Select in this manner also fails:
var itemIndexTwoTuples = "test".Select<char, Tuple<char, int>>(Tuple.Create);
What's the exact problem here?
First off, I note that this is a duplicate of:
Why is Func<T> ambiguous with Func<IEnumerable<T>>?
What's the exact problem here?
Thomas's guess is essentially correct. Here are the exact details.
Let's go through it a step at a time. We have an invocation:
"test".Select<char, Tuple<char>>(Tuple.Create);
Overload resolution must determine the meaning of the call to Select. There is no method "Select" on string or any base class of string, so this must be an extension method.
There are a number of possible extension methods for the candidate set because string is convertible to IEnumerable<char> and presumably there is a using System.Linq; in there somewhere. There are many extension methods that match the pattern "Select, generic arity two, takes an IEnumerable<char> as the first argument when constructed with the given method type arguments".
In particular, two of the candidates are:
Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,Tuple<char>>)
Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,int,Tuple<char>>)
Now, the first question we face is are the candidates applicable? That is, is there an implicit conversion from each supplied argument to the corresponding formal parameter type?
An excellent question. Clearly the first argument will be the "receiver", a string, and it will be implicitly convertible to IEnumerable<char>. The question now is whether the second argument, the method group "Tuple.Create", is implicitly convertible to formal parameter types Func<char,Tuple<char>>, and Func<char,int, Tuple<char>>.
When is a method group convertible to a given delegate type? A method group is convertible to a delegate type when overload resolution would have succeeded given arguments of the same types as the delegate's formal parameter types.
That is, M is convertible to Func<A, R> if overload resolution on a call of the form M(someA) would have succeeded, given an expression 'someA' of type 'A'.
Would overload resolution have succeeded on a call to Tuple.Create(someChar)? Yes; overload resolution would have chosen Tuple.Create<char>(char).
Would overload resolution have succeeded on a call to Tuple.Create(someChar, someInt)? Yes, overload resolution would have chosen Tuple.Create<char,int>(char, int).
Since in both cases overload resolution would have succeeded, the method group is convertible to both delegate types. The fact that the return type of one of the methods would not have matched the return type of the delegate is irrelevant; overload resolution does not succeed or fail based on return type analysis.
One might reasonably say that convertibility from method groups to delegate types ought to succeed or fail based on return type analysis, but that's not how the language is specified; the language is specified to use overload resolution as the test for method group conversion, and I think that's a reasonable choice.
Therefore we have two applicable candidates. Is there any way that we can decide which is better than the other? The spec states that the conversion to the more specific type is better; if you have
void M(string s) {}
void M(object o) {}
...
M(null);
then overload resolution chooses the string version because string is more specific than object. Is one of those delegate types more specific than the other? No. Neither is more specific than the other. (This is a simplification of the better-conversion rules; there are actually lots of tiebreakers, but none of them apply here.)
Therefore there is no basis to prefer one over the other.
Again, one could reasonably say that sure, there is a basis, namely, that one of those conversions would produce a delegate return type mismatch error and one of them would not. Again, though, the language is specified to reason about betterness by considering the relationships between the formal parameter types, and not about whether the conversion you've chosen will eventually result in an error.
Since there is no basis upon which to prefer one over the other, this is an ambiguity error.
It is easy to construct similar ambiguity errors. For example:
void M(Func<int, int> f){}
void M(Expression<Func<int, int>> ex) {}
...
M(x=>Q(++x));
That's ambiguous. Even though it is illegal to have a ++ inside an expression tree, the convertibility logic does not consider whether the body of a lambda has something inside it that would be illegal in an expression tree. The conversion logic just makes sure that the types check out, and they do. Given that, there's no reason to prefer one of the M's over the other, so this is an ambiguity.
You note that
"test".Select<char, Tuple<char>>(Tuple.Create<char>);
succeeds. You now know why. Overload resolution must determine if
Tuple.Create<char>(someChar)
or
Tuple.Create<char>(someChar, someInt)
would succeed. Since the first one does and the second one does not, the second candidate is inapplicable and eliminated, and is therefore not around to become ambiguous.
You also note that
"test".Select<char, Tuple<char>>(x=>Tuple.Create(x));
is unambiguous. Lambda conversions do take into account the compatibility of the returned expression's type with the target delegate's return type. It is unfortunate that method groups and lambda expressions use two subtly different algorithms for determining convertibility, but we're stuck with it now. Remember, method group conversions have been in the language a lot longer than lambda conversions; had they been added at the same time, I imagine that their rules would have been made consistent.
I'm guessing the compiler is confused by Tuple.Create<char, int>(char, int), but its return-type is wrong: it returns a two-tuple.
The return type isn't part of the method signature, so it isn't considered during overload resolution; it's only verified after an overload has been picked. So as far as the compiler knows, Tuple.Create<char, int>(char, int) is a valid candidate, and it is neither better nor worse than Tuple.Create<char>(char), so the compiler can't decide.

Why can't the compiler infer this type argument from usage

This question is based off of the results from another SO question of mine. My new question is not a question of how can I get X to work, but why doesn't X work.
I've created a simplified example of my problem, however if you want to see the practical application/situation I'm using this, look at my original question (the below functions don't actually do anything useful).
T bar<T>(Func<T, bool> f) { return default(T); }
bool foo(int i) { return true; }
Now I have 3 lines of code that do work as expected and all do the same thing purpose wise.
int num;
num = bar<int>(foo);
num = bar(new Func<int, bool>(foo));
num = bar((int i) => true );
My question is "Why do I need to explicitly specify T for bar for the first example?" The reason I wonder this is because the compiler turns the first two examples into the same line of code. Using ILSpy I see that the code compiles to this.
num = Program.bar<int>(new Func<int, bool>(Program.foo));
num = Program.bar<int>(new Func<int, bool>(Program.foo));
num = Program.bar<int>((int i) => true);
I don't understand why the compiler can't infer the type from the fact that I only have one function called foo, and it does fit the template so to speak. Now if I had created another function bool foo(bool i) I would understand if the compiler complained that there was some ambiguity and it didn't know which one I wanted and that I should specify the type argument explicitly.
This of course is just me being lazy, but it is just something I was expecting and was surprised when the compiler wasn't picking up my slack.
I'm on the bus, so, short answer.
type inference of t requires knowing the formal parameter type of the delegate type in the argument.
conversion of the method group to the delegate type does overload resolution as though the method group was invoked with the arguments of the formal parameter types of the target delegate.
but those types are what we are trying to deduce!
this is circular reasoning, so type inference rejects it. A method group conversion requires that the formal parameter types be deduced from some other argument.
you have no other arguments.
so inference fails.
that the group contains only one method is irrelevant. It would be bizarre if adding more methods to the group caused inference to fail. The rule that a group of overloads is resolved through overload resolution is a sensible one, and that requires knowing the parameters. You don't get to run the inferences backwards just because a method group happens to be a singleton.
see the type inference tag on my msdn blog for a longer article on this topic.
http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

Overloaded function and parameter of different types

One more question from the series "why does VisualStudio not support this code?"...
Given an overloaded function
private string ConfigQuery(string username) {
return "A";
}
private string ConfigQuery(int configId) {
return "B";
}
the following code is not possible:
public Config ConfigAPI(int id=0) {
string s = ConfigQuery(id==0?User.Identity.Name:id);
}
but the code
public Config ConfigAPI(int id=0) {
string s = (id==0?ConfigQuery(User.Identity.Name):ConfigQuery(id));
}
is. Is this reasonable behaviour? Then why would I use an overloaded function at all, instead of giving them more appropriate names like UserNameConfigQuery, ConfigIdConfigQuery?
Generally speaking, with a few exceptions such as the use of reflection, you are correct, there really isn't any "functionality" in overloading a method as opposed to creating a new method with a different name entirely. This is because, as has already been said, overload resolution is done at compile time (again, there are a few rare exceptions to this which should, generally speaking, be avoided) rather than at run time.
So you really aren't gaining anything, in terms of functionality, over giving each method a different name. It's mostly for convenience as a programmer, and to make it clear to users of your type that the method does the same thing, it just does it using one of several options.
Overloading is resolved at compile time, not at run time. The ternary operator ?: is evaluated at runtime. So at compile time, compiler doesn't know which function overload to link to as it doesn't know if id==0 or not. That's why your second example works because which overload called there can be resolved at compile time.
The reason for overloading is convenience of calling the same logical function with different number or type of parameters.
Yes, this is reasonable behavior.
Remember that the overload to call is determined at compile time vs. runtime. id==0? will not be evaluated until runtime of course.
You might still want to use overloads for:
same operations for different types (e.g. myString.Replace('1', '5') vs. myString.Replace("1", "5")) instead of using generics
optional/default params (e.g. new Foo(bar) vs. new Foo(bar, baz) or myRegex.Replace("blah", "yada") vs. myRegex.Replace("blah", "yada", 2)) instead of formal support that C# has added for optional and default parameters where they may make sense too
Also, IntelliSense, code documentation, and (formal or navigational) searches will help a lot less with differently-named methods that really do (nearly) the same thing, just differ on type or number of parameters. For example, consider finding and understanding the (near-)sameness of:
QueryConfig(string username) and QueryConfig(int configId) using overloads versus UserNameConfigQuery(string username) and ConfigIdConfigQuery(int configId) using "more appropriate" names
Replace(string input, string replacement) and Replace(string input, string replacement, int count) using overloads versus CountRestrictedReplace(string input, string replacement, int count) and UnrestrictedReplace(string input, string replacement) using "more appropriate" names
You did not post the error you got, but by the look of it the failure has nothing to do with overloading.
The error is because the ternary operator ? cannot get a string and int arguments, they need to be of the same type or with an implicit cast between them.
this post explains it rather well.
Edit:
From the spec, you can understand the reason for the failure:
If x has type X and y has type Y then:
If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.

Why can we use task like this?

task.ContinueWith( x => Process(x));
task.ContinueWith( Process)
I am wondering why both can work?
I thought ContinueWith needs at least one parameter of Task
The lambda expression is being converted to a method group.
13.6 Method group conversions
Similar to the implicit anonymous method conversions described in §13.5, an implicit conversion exists
from a method group (§14.1) to a compatible delegate type. If D is a
delegate type, and E is an expression that is classified as a method
group, then D is compatible with E if and only if E contains at least
one method that is applicable in its normal form (§14.4.2.1) to any
argument list (§14.4.1) having types and modifiers matching the
parameter types and modifiers of D.
The compile-time application of
the conversion from E to D is the same as the compile-time processing
of the delegate creation expression new D(E) (§14.5.10.3). Note that
the existence of an implicit conversion from E to D just indicates
that the set of applicable methods is not empty, but does not
guarantee that the compile-time application of the conversion will
succeed without error.
See http://en.csharp-online.net/ECMA-334:_13.6_Method_group_conversions for examples.
Those two lines are essentially the same thing. the lower one is a method call, and the lambda expression above it is just being converted into a similar method call. Same thing, just expressed differently.
Because ContinueWith expects an Action<Task> as parameter and Process has the right signature as well as (x) => Process(x).
x => Process(x) is creating a delegate.
A delegate is a type that references a method.
A Task represents an asynchronous operation.
An Action is a type of delegate.

Categories

Resources