Является ли интерфейс IComparable устаревшим / «вредным»?

11

IComparable работает только в одну сторону

Допустим, у вас есть Employeeкласс. В одном представлении вы хотите показать все Employeesотсортированные по имени - в другом по адресу. Как вы собираетесь достичь этого? Не с IComparable, по крайней мере, никаким идиоматическим способом.

IComparable имеет логику в неправильном месте

Интерфейс используется при вызове .Sort(). В представлении, отображающем Customerотсортированный по имени, вообще нет кода, который бы указывал, как он будет сортироваться.
С другой стороны, Customerкласс предполагает, как он будет использоваться - в этом случае он будет использоваться в списке, отсортированном по именам.

IComparable используется неявно

По сравнению с альтернативами очень трудно увидеть, где используется логика сравнения - или вообще. Предполагая вашу стандартную среду разработки и начиная с Customerкласса, мне придется

  1. Поиск всех ссылок на Customer
  2. Найдите те ссылки, которые используются в списке
  3. Проверьте, .Sort()вызывали ли эти списки их

Что еще хуже, если вы удалите IComparableреализацию, которая все еще используется, вы не получите никаких ошибок или предупреждений. Единственное, что вы получите, - это неправильное поведение во всех местах, которые были слишком неясны для вас.

Эти проблемы объединены, плюс меняющиеся требования

Сама причина, по которой я начал думать об этом, заключается в том, что для меня это пошло не так. Я успешно использую IComparableв своем приложении в течение 2 лет. Теперь требования изменились, и вещь должна быть отсортирована двумя разными способами. Заметили, что неинтересно проходить шаги, описанные в предыдущем разделе.

Вопрос

Эти проблемы заставляют меня думать, IComparableчто я уступаю IComparerили .OrderBy(), до такой степени, что не вижу ни одного действительного варианта использования, который не будет лучше обслуживаться альтернативами.
Всегда ли лучше использовать IComparerили LINQ, или я не вижу здесь преимуществ / вариантов использования?

Р. Шмитц
источник
2
Ваше новое требование "сортировать двумя разными способами" - это красная сельдь. Чтобы решить эту проблему, все, что вам нужно сделать, это передать другой компаратор в функцию сортировки.
Роберт Харви
@RobertHarvey Тогда ты больше не будешь использовать IComparable, что подтверждает мою точку зрения.
Р. Шмитц
Не забывайте, что если вы используете SortedXXXколлекции, они либо требуют, чтобы сохраненные элементы были, IComparableлибо чтобы они были IComparerпредоставлены. Также обратите внимание, что тривиально изменить порядок естественной сортировки с помощью одного компаратора и заставить его работать со всеми IComparableобъектами.
Берин Лорич
2
Неважно, что есть два разных интерфейса. IComparableсчитается механизмом сравнения по умолчанию . IComparerиспользуется, когда вы хотите переопределить механизм сравнения по умолчанию.
Роберт Харви
Пример ReverseComparer<T>: gist.github.com/jackfarrington/078e7af7bc82482aa634
Берин Лорич,

Ответы:

14

IComparableимеет ограничения, которые вы упомянули, это правильно. Это интерфейс, который уже был доступен в .NET Framework 1.0, где эти функциональные альтернативы и Linq были недоступны. Так что да, это можно рассматривать как устаревший элемент инфраструктуры, который в основном сохраняется для обратной совместимости.

Однако для многих простых структур данных один из способов сортировки, вероятно, достаточно или естественен. В этих случаях наличие одного канонического места для реализации отношения порядка все еще является хорошим способом сохранить код СУХИМ, вместо того, чтобы всегда повторять одну и ту же логику при каждом вызове OrderByповсюду.

Как вы писали, вы «счастливо используете IComparable в своем приложении уже 2 года», так что, как мне кажется, он долгое время служил вам хорошо. Когда вам теперь нужно проверять, изменять и тестировать все вызовы Sort, это также может быть признаком того, что вы выполняете одну и ту же логику сортировки во многих местах, что не является ошибкой IComparable. Так что это может быть поводом для централизации большей части этой логики в одном месте, делая ваш код более СУХИМЫМ.

Док Браун
источник
Хороший вопрос о простых структурах данных. Однако последний абзац не имеет для меня 100% смысла. Если бы я не использовал IComparable, весь существующий код сортировки остался бы нетронутым в их соответствующих представлениях, в то время как я бы добавил только новый код сортировки для нового представления.
Р. Шмитц
@ R.Schmitz Сработала бы уже существующая сортировка без IComparableнаписанной вами реализации?
Роберт Харви
3
@ R.Schmitz: Конечно, но теперь вы всегда готовы предоставить компаратор (если, конечно, вы не используете OrderBy). С помощью IComparableвы получаете реализацию по умолчанию бесплатно, а иногда вам даже не нужно писать эту реализацию.
Роберт Харви
2
@ R.Schmitz: Ваш последний комментарий хорошо подводит итог. Я бы пошел немного дальше, хотя. Предположим, у вас есть числовой тип, как BigInteger. Если бы он не реализовывал операторы / интерфейсы сравнения, как бы вы реализовали IComparer самостоятельно ? Вам понадобится доступ к внутренним структурам данных, чтобы сделать это эффективно или вообще. Предположим, у вас есть такой тип, как Customer; общедоступные свойства, по которым вы хотите отсортировать , имеют компараторы. Для меня в этом разница: реализовать, IComparable<T>если было бы неразумно ожидать, что вызывающая сторона будет реализовывать компаратор.
Эрик Липперт
1
If I hadn't used IComparable, all the pre-existing sorting code would have been left untouched in their respective views, while I'd only add new sorting code for the new view.Просто потому что IComparableбыл лучше в то время это решение , не означает, что это лучшее решение сегодня . Ваш первый комментарий здесь подразумевает, что «это сопоставимо или ничего», что не соответствует действительности, проблема могла быть решена различными способами. Приложения могут увеличиваться в размере / масштабе, а вещи, которые раньше выглядели подходящими, могут не соответствовать растущим требованиям приложения.
Флэттер
1

Я согласен с вашими чувствами по поводу IComparable

Просто посмотрите на замечания по Array.Sort()

  • Каждый элемент массива должен реализовывать IComparable интерфейс для возможности сравнения с любым другим элементом массива. (или исключение выдается)
  • Если сортировка не была успешно завершена, результаты не определены.

Мы, вероятно, никогда не будем сейчас мотивации, однако! рассматриватьobject.Equals() метод для каждого объекта, который позволяет сравнивать объекты друг с другом, чтобы увидеть, являются ли они «одинаковыми»

У вас это уже есть, но вам нужно было добавить, что Array.Sort()вы можете добавитьobject.Compare(object)

Ewan
источник