比较实现IComparable的项目的问题

我正在研究一种扩展方法,它通过特定的选择器找到最小项目。 代码下面

public static T MinBy(this IEnumerable src, Func selector) where K : struct, IComparable, IConvertible { var min = default(K); T minItem = default(T); foreach (var item in src) { var current = selector(item); if (current < min) { min = current; minItem = item; } } return minItem; } 

它给出错误Error Operator '<' cannot be applied to operands of type 'K' and 'K' 。 但是我已经指定了通用约束K应该是Struct and IComparable 。 我相信所有数字数据类型都可以满足于此。

那为什么这是一个无效的操作。

IComparable没有(也不能)对运营商说些什么。 你应该使用:

 if (current.CompareTo(min) < 0) 

运算符是静态的,只是过而不是重载 。 您不能在接口中要求运算符,并且方法的存在不会神奇地改变运算符将执行的操作。 (例如,重写Equals不会改变==行为的方式。)

您还应该注意,由于您的约束仅涉及非通用IComparable接口,因此您将在每次操作时进行装箱。 我建议您将约束更改为IComparable 。 (或者删除约束,然后像Marc建议的那样使用Comparer.Default 。)

关于您的方法的一些其他评论:

  • 如果所有键值都大于K的默认值(例如K = int且所有键都为正),那么您将找不到项目
  • 您可能希望有一个接受特定IComparare的重载(但仅当您删除可比较的约束时)
  • 没有必要将K约束为值类型。 如果我想找到具有词典最早名称的人,该怎么办?
  • 如果没有元素,它将返回T的默认值; 为了适应LINQ的其余部分,我建议抛出InvalidOperationException
  • 我建议使用TSourceTKey作为类型参数,以便与LINQ更加一致

您可能希望将MoreLINQ MinBy实现视为替代方案。 (再看看,我不确定我们要求comparer是非空的;如果comparer为空,它应该使用默认比较器,就像普通LINQ一样。)

IComparable不提供操作员支持 – 您需要使用current.CompareTo(min) 。 或者更好的是,使用Comparer.Default.Compare(current,min) – 然后你可以删除约束它将自动处理空值等,它将避免装箱。

 var comparer = Comparer.Default; ... // loop if(comparer.Compare(current, min) < 0) {...}