Проблема сравнения элементов, реализующих IComparable

Я работаю над методом расширения, в котором он находит минимальный элемент по определенному селектору. Под кодом

    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. Я считаю, что все числовые типы данных могут быть удовлетворены этим.

Тогда почему это недопустимая операция?


person RameshVel    schedule 24.02.2011    source источник


Ответы (2)


IComparable ничего не говорит (и не может) об операторах. Вы должны использовать:

if (current.CompareTo(min) < 0)

Операторы являются статическими и всегда перегружаются, а не переопределяются. Вы не можете требовать операторов внутри интерфейсов, и наличие метода не волшебным образом меняет то, что будет делать оператор. (Например, переопределение Equals не меняет поведение ==.)

Вы также должны отметить, что, поскольку ваше ограничение говорит только о неуниверсальном интерфейсе IComparable, вы будете упаковывать его при каждой операции. Вместо этого я предлагаю вам изменить ограничение на IComparable<K>. (Или отмените ограничение и просто используйте Comparer<K>.Default, как предложил Марк.)

Некоторые другие комментарии о вашем методе:

  • Если все значения ключа больше, чем значение по умолчанию для K (например, K=int и все ключи положительные), вы не найдете элемент
  • Вы можете захотеть иметь перегрузку, которая принимает конкретное IComparare<K> (но только если вы отбросите сопоставимое ограничение)
  • Нет реальной необходимости ограничивать K типом значения. Что если я хочу найти человека с лексикографически самым ранним именем?
  • Если элементов нет, будет возвращено значение по умолчанию для T; чтобы соответствовать остальной части LINQ, я бы предложил бросить InvalidOperationException
  • Я бы предложил использовать TSource и TKey в качестве параметров типа, чтобы они были более совместимы с LINQ.

Вы можете рассмотреть реализацию MoreLINQ MinBy. альтернатива. (Просматривая это снова, я не уверен, что нам стоит требовать, чтобы comparer не было нулевым; вероятно, он должен использовать компаратор по умолчанию так же, как это делает обычный LINQ, если компаратор равен нулю.)

person Jon Skeet    schedule 24.02.2011
comment
Например, переопределение Equals действительно меняет поведение == - это, вероятно, должно означать, что не изменяется? - person Groo; 28.08.2015

IComparable не обеспечивает поддержку оператора — вам нужно использовать current.CompareTo(min). Или, что лучше, используйте Comparer<T>.Default.Compare(current,min) — тогда вы можете удалить ограничение, и оно будет автоматически обрабатывать пустые значения и т. д., и оно избежит упаковки.

var comparer = Comparer<T>.Default;
...
// loop
    if(comparer.Compare(current, min) < 0) {...}
person Marc Gravell    schedule 24.02.2011