6

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

    public static T MinBy<T, K>(this IEnumerable<T> src, Func<T, K> 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. 我相信所有的数值数据类型都可以满足于此。

那怎么会是无效操作呢?

4

2 回答 2

19

IComparable没有(也不能)说任何关于运营商的事情。你应该使用:

if (current.CompareTo(min) < 0)

运算符是静态的,并且只会被重载而不是被覆盖。您不能在接口中要求运算符,并且方法的存在不会神奇地改变运算符将做什么。(例如,覆盖Equals不会改变==行为方式。)

您还应该注意,由于您的约束仅涉及非泛型IComparable接口,因此您将在每个操作中进行装箱。我建议您将约束更改为IComparable<K>。(或者放弃约束并Comparer<K>.Default按照 Marc 的建议使用。)

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

  • 如果所有键值都大于默认值K(例如 K=int 并且所有键都是正数),那么您将找不到项目
  • 您可能希望有一个接受特定的重载IComparare<K>(但前提是您放弃了可比较的约束)
  • 没有真正需要限制K为值类型。如果我想找到字典上名字最早的人怎么办?
  • 如果没有元素,它将返回默认值T;为了适应 LINQ 的其余部分,我建议扔InvalidOperationException
  • 我建议使用TSourceandTKey作为类型参数以更符合 LINQ

您可能希望查看MoreLINQ MinBy 实现作为替代方案。(再看一遍,我不确定要求它comparer是非空的对我们来说是个好主意;如果比较器为空,它可能应该以与普通 LINQ 相同的方式使用默认比较器。)

于 2011-02-24T06:57:31.237 回答
11

IComparable不提供运营商支持 - 您需要使用current.CompareTo(min). 或者更好的是,使用Comparer<T>.Default.Compare(current,min)- 然后你可以删除约束它会自动处理空值等,会避免装箱。

var comparer = Comparer<T>.Default;
...
// loop
    if(comparer.Compare(current, min) < 0) {...}
于 2011-02-24T06:57:49.617 回答