Why is Linq.Sum () with decimal places ambiguous between int and int?

short:
I have a decimal GetAmount(IThing thing) method. Using things.Sum(GetAmount) results in error CS0121: the call is ambiguous between Sum(Func<T, int>) and Sum(Func<T, int?>) . Why?

more

 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 is confused between decimal and decimal? , or if the compiler was unable to select any of the many Sum overloads. But why / how was it limited only to int and int?

Btw, VS / R # "useful" distinguishes the GetAmount transfer to .Sum() as an error and suggests passing the int return method instead. The compiler does not indicate this part as a second error. Changing GetAmount to int GetAmount(IThing thing) actually "solves" the compiler error.

ps. I am not looking for a solution to a compiler error. I know that I can turn GetAmount into Func<IThing, decimal> GetAmount { get; } Func<IThing, decimal> GetAmount { get; } or implement Total as things.Sum(thing => GetAmount(thing)) . And, as @ Ivanvan suggested, things.Sum works (for me) things.Sum(new Func<IThing, decimal>(GetAmount)) .

+5
source share
2 answers

You simply do not have .Sum() overloads where you can pass your method.

You are right, you can do this:

 things.Sum(thing => GetAmount(thing)) 

thing => GetAmount(thing) - this part will basically create the anonymouse function, and .Sum() will get the overload for it.

One of the other ways to implement it (more obviouse so you can understand what is actually happening) is to 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 am using VS 2015).

Severity Code Description Project File Line Suppression State Error CS0407 'decimal Sample.GetAmount (IThing)' has incorrect return type

So, I think that you get this wired error only because the pre-compiler analyzer is not perfect.

I did a bit more research and tried to compile your code without a precompiler from the command line, for example:

C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ csc.exe / t: exe /out:Program.exe Program.cs

And now the compiler will return the correct 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 can see, we got the correct error with the decimal type. Thus, the wied compiler error that we get somewhere in the sources of the preliminary compiler.

+4
source

The error message associated with CS0121 displays only the first two ambiguous matches. The error applies to all Sum signatures. You can prove this yourself by writing an extension class that matches the Sum signature and switches 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 the method is not considered part of its signature. Again, you can prove this by writing two methods with the same name that differ only in the inverse 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 

Therefore, when you look for Sum overload that accepts Func with the correct signature, only parameters are considered, not return types, in this case IThing , which leads to the fact that it matches all Sum overloads and, therefore, is ambiguous.

+1
source

Source: https://habr.com/ru/post/1261373/


All Articles