Проблема сравнения элементов, реализующих IComparable - PullRequest
5 голосов
/ 24 февраля 2011

Я работаю над методом расширения, где он находит элемент min по определенному селектору.Ниже кода

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

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

Ответы [ 2 ]

17 голосов
/ 24 февраля 2011

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, если компаратор равен нулю.)

9 голосов
/ 24 февраля 2011

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

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