为什么Linq.Sum()的小数在int和int之间是不明确的?

时间:2016-12-16 08:03:25

标签: c# linq compiler-errors overload-resolution

短:
我有一个方法decimal GetAmount(IThing thing)。使用things.Sum(GetAmount)会导致CS0121错误:Sum(Func<T, int>)Sum(Func<T, int?>)之间的通话不明确。为什么呢?

长:

public interface IThing { }
public class Sample {
    public decimal GetAmount(IThing thing) { return 0; }
    public decimal Total(IThing[] things) { return things.Sum(GetAmount); }
}
  

错误CS0121以下方法或之间的调用不明确   属性:'Enumerable.Sum<TSource>(IEnumerable<TSource>, Func<TSource, int>)'和   'Enumerable.Sum<TSource>(IEnumerable<TSource>, Func<TSource, int?>)'

我会理解编译器是否在decimaldecimal?之间混淆,或者编译器是否无法选择任何多次Sum重载。但是为什么/如何将自己仅限于intint?

顺便说一下,VS / R#&#39;帮助&#39;突出显示将GetAmount传递给.Sum()作为错误,并建议传递一个int返回方法。编译器不会将此部分标记为第二个错误。将GetAmount更改为int GetAmount(IThing thing)实际上可以“解决”问题。编译错误。

PS。我不是在寻找编译器错误的解决方案。我知道我可以将GetAmount转换为Func<IThing, decimal> GetAmount { get; }或实现Total things.Sum(thing => GetAmount(thing))。正如@IvanStoev所建议的那样,things.Sum(new Func<IThing, decimal>(GetAmount))也适合(对我来说)。

2 个答案:

答案 0 :(得分:4)

您只是没有.Sum()的重载,您可以在其中传递您的方法。

你是对的,你可以这样做:

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

thing => GetAmount(thing) - 这部分基本上会创建匿名函数,.Sum()会超载。

实现它的其他方法之一(更明确的方式让你可以理解实际发生的事情)是自己创建func:

public decimal Total(IThing[] things) 
{ 
   return things.Sum(new Func<IThing, decimal>(GetAmount)); 
}

实际上我的代码出现了另一个编译器错误(我使用的是VS 2015)。

  

严重级代码描述项目文件行抑制状态   错误CS0407'十进制Sample.GetAmount(IThing)'返回错误   输入

所以我认为你得到的有线错误只是因为预编译器分析器并不完美。

我做了一些研究,尝试在没有预编译器的情况下从命令提示符编译代码,如下所示:

  

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

现在编译器返回错误:

  

Program.cs(13,56):错误CS0121:之间的调用不明确   以下方法或属性:   'System.Linq.Enumerable.Sum<Lambda.IThing>(System.Collections.Generic.IEnumerable<Lambda.IThing>, System.Func<Lambda.IThing,decimal>)'和   'System.Linq.Enumerable.Sum<Lambda.IThing>(System.Collections.Generic.IEnumerable<Lambda.IThing>, System.Func<Lambda.IThing,decimal?>)'

正如您现在所看到的,decimal类型的错误是正确的。所以我们在预编译器源中找到了一个奇怪的编译器错误。

答案 1 :(得分:1)

与CS0121关联的错误消息仅显示前两个不明确的匹配项。该错误适用于Sum的所有签名。您可以通过编写与Sum签名匹配的扩展类并切换类中方法的顺序来证明这一点:

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>)'

方法的返回类型不被视为其签名的一部分。再次,您可以通过编写两个具有相同名称但只返回类型不同的方法来证明这一点:

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

因此,当查找Sum的重载接受带有正确签名的Func时,只会考虑参数,而不是返回类型,在这种情况下IThing会导致它匹配Sum的所有重载,因此不明确。