比较实现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
。)
关于您的方法的一些其他评论:
- 如果所有键值都大于
K
的默认值(例如K = int且所有键都为正),那么您将找不到项目 - 您可能希望有一个接受特定
IComparare
的重载(但仅当您删除可比较的约束时) - 没有必要将
K
约束为值类型。 如果我想找到具有词典最早名称的人,该怎么办? - 如果没有元素,它将返回
T
的默认值; 为了适应LINQ的其余部分,我建议抛出InvalidOperationException
- 我建议使用
TSource
和TKey
作为类型参数,以便与LINQ更加一致
您可能希望将MoreLINQ MinBy实现视为替代方案。 (再看看,我不确定我们要求comparer
是非空的;如果comparer
为空,它应该使用默认比较器,就像普通LINQ一样。)
IComparable
不提供操作员支持 – 您需要使用current.CompareTo(min)
。 或者更好的是,使用Comparer
– 然后你可以删除约束,它将自动处理空值等,它将避免装箱。
var comparer = Comparer.Default; ... // loop if(comparer.Compare(current, min) < 0) {...}