short:
I have a method decimal GetAmount(IThing thing)
. Using things.Sum(GetAmount)
results in a CS0121 error: call is ambiguous between Sum(Func<T, int>)
and Sum(Func<T, int?>)
. Why?
longer:
public interface IThing { }
public class Sample {
public decimal GetAmount(IThing thing) { return 0; }
public decimal Total(IThing[] things) { return things.Sum(GetAmount); }
}
Error CS0121 The call is ambiguous between the following methods or properties:
'Enumerable.Sum<TSource>(IEnumerable<TSource>, Func<TSource, int>)'
and'Enumerable.Sum<TSource>(IEnumerable<TSource>, Func<TSource, int?>)'
I would somewhat understand if the compiler was confused between decimal
and decimal?
or if the compiler couldn't pick any of the many Sum overloads. But why/how did it limit itself to only int
and int?
Btw, VS / R# 'helpfully' highlights passing GetAmount
to .Sum()
as an error and suggests passing an int returning method instead. The compiler doesn't flag this part as a second error. Changing GetAmount
to int GetAmount(IThing thing)
actually does 'solve' the compiler error.
ps. I'm not looking for a solution to the compiler error. I know I can turn GetAmount into Func<IThing, decimal> GetAmount { get; }
or implement Total like things.Sum(thing => GetAmount(thing))
. And as suggested by @IvanStoev, things.Sum(new Func<IThing, decimal>(GetAmount))
works (for me) as well.
You just don't have overload of .Sum()
where you can pass your method.
You right you can do it that way:
things.Sum(thing => GetAmount(thing))
thing => GetAmount(thing)
- this part basically will create anonymouse function and .Sum()
got overload for it.
One of the other ways to implement it (more obviouse way so you can understand what actually happends) is create func yourself:
public decimal Total(IThing[] things)
{
return things.Sum(new Func<IThing, decimal>(GetAmount));
}
Actually i get another compiler error with your code (I use VS 2015).
Severity Code Description Project File Line Suppression State Error CS0407 'decimal Sample.GetAmount(IThing)' has the wrong return type
So I think you get that wired error only becouse precompiler analizer is not perfect.
I did a bit more research and try to compile your code without precompiler from command prompt like this:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /t:exe /out:Program.exe Program.cs
And now compiler return right error:
Program.cs(13,56): error CS0121: The call is ambiguous between the following methods or properties:
'System.Linq.Enumerable.Sum<Lambda.IThing>(System.Collections.Generic.IEnumerable<Lambda.IThing>, System.Func<Lambda.IThing,decimal>)'
and'System.Linq.Enumerable.Sum<Lambda.IThing>(System.Collections.Generic.IEnumerable<Lambda.IThing>, System.Func<Lambda.IThing,decimal?>)'
As you see now we got right error with decimal
type. So wierd compiler error that we get located somewhere in precompiler sources.
The error message associated with CS0121 only displays the first two ambiguous matches. The error applies to all signatures of Sum
. You can prove this yourself by writing an extension class that matches the Sum
signature and switching the order of the methods within the class:
public static class Extensions
{
public static decimal TestSum<T>(this IEnumerable<T> source, Func<T, decimal> selector)
{
return 0;
}
public static int TestSum<T>(this IEnumerable<T> source, Func<T, int> selector)
{
return 0;
}
public static int? TestSum<T>(this IEnumerable<T> source, Func<T, int?> selector)
{
return 0;
}
}
CS0121 The call is ambiguous between the following methods or properties:
'Extensions.TestSum<T>(IEnumerable<T>, Func<T, decimal>)' and
'Extensions.TestSum<T>(IEnumerable<T>, Func<T, int>)'
The return type of a method is not considered to be part of it's signature. Again you can prove this by writing two methods with the same name that only differ in return type:
public class TestClass
{
public decimal TestReturn(int value) { return 0m; }
public int TestReturn(int value) { return 0; }
}
CS0111 Type 'TestClass' already defines a member called 'TestReturn' with the same parameter types
So when looking for an overload of Sum
that accepts a Func
with the correct signature, only parameters are considered, not return types, in this case IThing
, which causes it to match all overloads of Sum
and therefore be ambiguous.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With