Коли використовувати IComparable <T> Vs. IComparer <T>


101

Я намагаюся розібратися, який із цих інтерфейсів мені потрібно реалізувати. Вони обоє по суті роблять те саме. Коли я використовую один над іншим?


Чудові приклади та пояснення можна знайти тут: support.microsoft.com/nl-nl/help/320727/…
січня

Відповіді:


96

Ну, це не зовсім те саме, що IComparer<T>реалізовано для типу, який здатний порівнювати два різні об'єкти, а IComparable<T>реалізується на типах, які здатні порівнювати себе з іншими екземплярами того ж типу.

Я схильний використовувати IComparable<T>час, коли мені потрібно знати, як інший екземпляр стосується thisекземпляра. IComparer<T>є корисним для сортування колекцій у вигляді IComparer<T>стендів поза порівнянням.


9
IComparer <T> також дозволяє мати клас для кожного типу сорту, який ви хочете. Приклад; PersonLastFirstNameComparer, PersonFirstLastNameComparer або PersonAgeComparer.
Ерік Шнайдер

Чи є простий спосіб ви їх запам'ятовуєте? Я, як правило, повинен шукати це кожного разу.
амадіб

59
@amadib думаю, IComparableяк я порівняний . це означає, що мене можна порівняти з чимось іншим. І читайте, IComparerяк я порівняльник, я просто порівнюю, що означає, що я порівнюю деякі речі.
nawfal

@newfal Ви мали б поставити це як відповідь. Я думаю, що це найкраще пояснення тут.
Gene S

42

Використовуйте, IComparable<T>коли клас має внутрішнє порівняння.

Використовуйте, IComparer<T>коли ви хочете метод порівняння, відмінний від внутрішнього порівняння класу, якщо він має такий.


28

Це залежить від сутності. Наприклад, для наступного класу, як "Студент", має сенс мати ICпорівнянний на основі Імені.

class Student : IComparable 
{
    public string Name { get; set; }
    public int MathScore { get; set; }
    public int EnglishScore { get; set; }

    public int TotalScore 
    {
        get
        {
            return this.MathScore + this.EnglishScore; 
        }
    }

    public int CompareTo(object obj)
    {
        return CompareTo(obj as Student);  
    }

    public int CompareTo(Student other)
    {
        if (other == null)
        {
            return 1;
        }
        return this.Name.CompareTo(other.Name);  
    }
}

Але якщо вчитель "A" хоче порівняти учнів на основі MathScore, а вчитель "B" хоче порівняти учнів на основі EnglishScore. Непогано буде впровадити IComparer окремо. (Більше, як стратегія)

class CompareByMathScore : IComparer<Student>
{
    public int Compare(Student x, Student y)
    {
        if (x.MathScore > y.MathScore)
          return 1;
        if (x.MathScore < y.MathScore)
          return -1;
        else
          return 0;
    }
}

Зробіть метод порівняння статичним для простоти використання.
січня

9

Все залежить від того, мінливий ваш тип чи ні. Ви повинні тільки реалізувати IComparable на які не змінюються типів. Зауважте, що якщо ви реалізуєте IComparable, ви повинні перекрити рівність разом з операторами ==,! =, <І> (див. Попередження про аналіз коду CA1036).

Цитуючи Дейва Г з цієї публікації в блозі :

Але правильна відповідь полягає в тому, щоб застосувати IComparer замість IComparable, якщо ваші об'єкти є змінними, і передайте екземпляр IComparer для функцій сортування, коли це необхідно.

Оскільки IComparer - це просто одноразовий об'єкт, який використовується для сортування в цей момент часу, ваш об’єкт може мати будь-яку змінну семантику, яку ви бажаєте. Крім того, він не вимагає або навіть пропонує використовувати Equals, GetHashCode або == - ви можете визначити це будь-яким способом, яким ви хочете.

Нарешті, ви можете визначити кілька типів IComparer для вашого типу для сортування в різних полях або з різними правилами. Це набагато гнучкіше, ніж дотримуватися одного визначення.

Коротше кажучи: Використовуйте IComparable для типів значень та IComparer для еталонних типів.


6

Просте пояснення через розповідь

Баскетбол середньої школи Це шкільний двір для команд. Я хочу отримати найвищих / найкращих / найшвидших людей у ​​своїй команді. Що мені робити?

Інтерфейс IComparer - Порівняйте двох людей, роздільних людей

  • Це дозволяє мені порівняти будь-яких двох хлопців, що вишикувалися ......... це в основному все. Фред проти Джона .......... я кидаю їх у конкретний клас, який реалізує інтерфейс. Compare(Fred, John)і це випльовує, хто кращий.

Що з IComparable? - Порівняйте себе з кимось іншим

Ви нещодавно були на ФБ? Ви бачите інших людей, які роблять класні речі: подорожують світом, створюють винаходи, в той час як я роблю щось не зовсім круте - ну, що ми робимо, це використовувати інтерфейс IComparable.

  • Ми порівнюємо поточний екземпляр (себе) з іншим об'єктом (кимось іншим), який є одного типу (людина).

А як щодо класу порівняння?

Клас Порівняння - це абстрактний базовий клас, який реалізує інтерфейс IComparer. Ви повинні випливати з цього класу, щоб мати конкретну реалізацію. у будь-якому випадку, Microsoft рекомендує вам використовувати клас Порівняння, а не реалізовувати інтерфейс IComparer:

Ми рекомендуємо виходити з класу Порівняння замість реалізації інтерфейсу IComparer, оскільки клас Порівняння забезпечує явну реалізацію інтерфейсу методу IComparer.Compare та властивості за замовчуванням, що отримує порівняння за замовчуванням для об'єкта.

Підсумок

  • IComparer - складіть дві речі та порівняйте.
  • Ізрівнянний - порівняйте себе з іншими на ФБ.

Сподіваюся, історії допоможуть вам запам'ятати.


1
Мені подобається твій спосіб ілюстрації ключової концепції. Це може бути краще, якщо ви включите в цей конкурс клас Порівняння (T). Навіть це не включається в питання. :)
Кевман

4

Як говорили інші, вони не роблять те саме.

У будь-якому випадку, в ці дні я, як правило, не користуюся IComparer. Чому б я? Її відповідальність (зовнішня сутність, яка використовується для порівняння двох об'єктів) може обробляти набагато чистіше з лямбда-висловом, аналогічно тому, як працює більшість методів LINQ. Напишіть швидку лямбда, яка бере об’єкти для порівняння як аргументи і повертає буль. І якщо об'єкт визначає власну операцію порівняння, він замість цього може реалізувати IComparable.


1
-1: повернення буля не рівнозначне IComparer. IComparer повертає значення, яке може бути менше нуля / нуля / більше нуля і зазвичай використовується для сортування.
Джо

І коли це те, що вам потрібно, ви повернете замість нього int (а ще краще - перерахунок). Це справді велика справа?
jalf

2
Ви навіть можете повернути bool, оскільки для сортування послідовності потрібна лише одна операція, яка вам потрібна.
jalf

реалізацію IComparer потрібно визначити лише один раз; якщо вам потрібно використовувати логіку сортування в декількох місцях, тоді вираз лямбда потрібно буде писати більше разів.
oɔɯǝɹ

oɔɯǝɹ - Тоді ви можете зберігати посилання на делегата, який був записаний у вигляді лямбда-виразу, також повторно використовувати його.
jpierson

3

IComparable каже, що об'єкт можна порівняти з іншим. IComparer - це об'єкт, який може порівняти будь-які два елементи.


2

IComparer - це інтерфейс, який використовується для сортування масиву, цей інтерфейс змусить клас реалізувати метод Порівняння (T x, T y), який порівняє два об'єкти. Екземпляр класу, який реалізував цей інтерфейс, використовується при сортуванні масиву.

IComparable - це інтерфейс, реалізований у тому типі, який повинен порівнювати два об'єкти одного типу. Цей порівнянний інтерфейс змусить клас реалізувати наступний метод CompareTo (T obj)

IEqualityComparer - це інтерфейс, який використовується для пошуку об’єкта, рівний він чи ні. Тепер ми побачимо це у прикладі, де нам належить знайти відмінність об’єкта в колекції. Цей інтерфейс реалізує метод рівний (T obj1, T obj2)

Тепер ми беремо приклад, у нас є клас Співробітник, на основі цього класу ми повинні створити колекцію. Тепер у нас є такі вимоги.

Сортування масиву за допомогою класу Array 2. Потрібна колекція за допомогою Linq: Видаліть дублікат, Упорядкуйте вище за нижчу, Видаліть один ідентифікатор співробітника

abstract public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { set; get; }
}

public enum SortType
{
    ByID,
    BySalary
}

публічний клас EmployeeIdSorter: IComparer {public int Порівняти (Employee x, Employee y) {if (x.Id <y.Id) return 1; інакше, якщо (x.Id> y.Id) повернути -1; інше повернути 0; }}

    public class EmployeeSalarySorter : IComparer<Employee>
    {
        public int Compare(Employee x, Employee y)
        {
            if (x.Salary < y.Salary)
                return 1;
            else if (x.Salary > y.Salary)
                return -1;
            else
                return 0;
        }
    }

Для отримання додаткової інформації референду нижче http://dotnetvisio.blogspot.in/2015/12/usage-of-icomparer-icomparable-and.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.