Подтвердить что ты не робот

Для чего используется класс Comparer <T>?

Какую цель выполняет класс Comparer<T>, если указанный тип уже реализует IComparable?

Если я укажу Comparer.Default, а Customer уже реализует IComparable, то почему я должен использовать класс Comparer?

4b9b3361

Ответ 1

Я думаю, что ваш вопрос в том, почему есть базовый класс, который, по-видимому, имеет только один полезный метод, который оказывается тем же методом, который вы бы реализовали, если бы вы внедрили интерфейс напрямую. Если бы я понял, что правильно, я думаю, вы правы, что нет никакой выгоды для получения Comparer<T>, а не для реализации IComparer<T> напрямую, за исключением того, что базовый класс предоставляет вам общую и не-общую реализацию с единственным переопределенным методом.

Но если ваш вопрос, почему есть как IComparer<T>, так и IComparable<T>, тогда как указывали другие, Comparer<T> позволяет вам определить различные способы выполнения сравнения. Это реализация шаблона проектирования стратегии. Отличным примером этого могут быть различные свойства StringComparer, такие как StringComparer.Ordinal, StringComparer.OrdinalIgnoreCase и т.д. Это позволяет вам сортировать строки по-разному в разных обстоятельствах, которые интерфейс IComparable<T> просто не может предвидеть.

Но помимо возможности переформулировать способ сравнения, иногда предоставление внешнего компаратора - единственный способ сделать это. Например, класс ListView Windows Forms позволяет указать IComparer для его логики сортировки. Но ListViewItem не реализует IComparable. Таким образом, без стратегии сравнения, которая знает, как это сделать, ListViewItem не сортируется, потому что у него нет стандартной реализации IComparable.

Итак, в конце концов, это просто еще одна точка расширяемости, которая позволяет вам больше гибкости, если вы не являетесь автором типа, который должен быть отсортирован (или вам нужны несколько стратегий сортировки.)


EDIT: В ответ на ваш комментарий ниже: "Если у меня есть класс, который реализует IComparer (мой собственный класс), это позволит мне сортировать любое количество свойств (собственный тип), почему тогда я буду беспокоиться об использовании Comparer.Default"

Возможно, пример поможет. Скажем, вы пишете метод расширения, который проверяет, существует ли заданное значение между диапазоном.

public static bool Between<T>(this T value, T minValue, T maxValue) {

    var comparer = Comparer<T>.Default;

    int c1 = comparer.Compare(value, minValue);
    int c2 = comparer.Compare(value, maxValue);

    return (c1 >= 0 && c2 <= 0);

}

В этом случае я ничего не знаю о типе T. Он может реализовать IComparable или может реализовать IComparable<T>, или он не может реализовать ни одно из них, и будет выведено исключение. Это также позволяет мне легко добавить перегрузку для этого метода, который позволяет вызывающему перейти в свой собственный компаратор. Но здесь полезен Comparer, потому что он позволяет мне получить сопоставление по умолчанию для неизвестного типа, который может или не может реализовывать общий или не общий интерфейс IComparable.

Ответ 2

Так как вам иногда нужно поддерживать упорядоченные порядки/упорядоченные очереди, то "естественный" порядок или более, то существует один естественный порядок.

Например, если у вас есть плоские линии, вы можете отсортировать их по:

  • Номер рейса
  • Пункт назначения
  • Время
  • Приоритет (некоторые рейсы могут пострадать от более длительных задержек, чем другие).
  • ...

Задачи на компьютере могут быть запланированы:

  • Пользователь
  • Приоритет (в планировщике)
  • PID (нормальное сравнение)
  • ...

Таким образом, даже в одном приложении вам может понадобиться отсортировать объекты по различным свойствам. Вы не можете сделать это с помощью метода int compareTo(Object), поскольку он не может различать между контекстами. Однако вы можете добавить контекст, то есть реализовать CompareByPriority.

Ответ 3

Тип не должен реализовывать IComparable, он может быть любого типа - нет ограничений на T:

public abstract class Comparer<T> : IComparer, IComparer<T>

Новый Comparer, который вы создаете, реализует IComparer<T> и не общий IComparer, и может использоваться для сравнения и сортировки коллекций.

Вы правы: если ваш тип Customer реализует IComparable, и вам не нужно другое сравнение, Comparer вам не пригодится. Большинство классов в инфраструктуре .net могут принимать как IComparable<T>, так и Comparer<T>, поэтому вы можете использовать один из них.

Однако вы ошибаетесь, полагая, что это всегда так. Очень возможно создать Comparer для не сравнимого типа. Обратите внимание, что требуется не:

public abstract class Comparer<T> : IComparer, IComparer<T>
                                    where T : IComparable, IComparable<T>

Предположим, что у вас есть простой класс Person, и вы хотите отсортировать список Persons, лучше всего его написать для сравнения:

public class Person
{
    string Name { get; set; }
}

Ответ 4

Здесь есть несколько тонких моментов:

  • он поддерживает не только IComparable<T> - он также поддерживает более старый (не общий) IComparable как резерв. Это означает, что он не может быть выражен так же, как (например) общее ограничение
  • он поддерживает Nullable<T>, где T сопоставим, хотя Nullable<T> явно не IComparable или IComparable<T>
  • он предотвращает взрыв ограничений общего типа, не требуя их - для примера, a List<T> может предоставить Sort, даже если он не настаивает на том, что все T являются сортируемыми; вы будете удивлены, как быстро общие ограничения будут накапливаться иначе.
  • он позволяет передавать сравнитель в любой из существующих API, требующих сравнения, когда все, что у вас есть, - это тип, который может быть сопоставим; большинство API-интерфейсов сортировки (включая LINQ) будут предлагать поддержку сравнения.

Ответ 5

public class Person
{
    public string LastName;
    public string FirstName;

}

public class Class2
{
    public void test()
    {
        List<Person> classList = new List<Person>();
        //add some data to the list
        PersonComparer comp = new PersonComparer();
        classList.Sort(comp);
    }
}

public class PersonComparer : Comparer<Person>
{

    public override int Compare(Person x, Person y)
    {
        int val = x.LastName.CompareTo(y.LastName);
        if (val == 0)
        {
            val = x.FirstName.CompareTo(y.FirstName);
        }
        return val;
    }
}

Ответ 6

Если тип реализует IComparable<T>, почти наверняка лучше использовать его, чем IComparable. С типами значений производительность IComparable<T> часто намного лучше, чем производительность не общего IComparable. С наследуемыми ссылочными типами IComparable<T> может предлагать лучшую семантику, чем IComparable, позволяя ранжирование на основе полей производного типа.

Для примера последнего преимущества предположим, что у одного есть абстрактный базовый класс ScheduleEvent с свойством EventTime, который реализует IComparable<ScheduleEvent> путем сортировки EventTime. Производные типы включают ScheduledPopupMessageEvent с строкой сообщения, a ScheduledGongEvent с параметром GongVolume. Несколько ScheduleEvent с тем же EventTime должны сообщать ноль для IComparable<ScheduleEvent>.CompareTo, потому что нет безопасного и последовательного способа ранжирования ScheduleEvent разных типов, а также потому, что два ScheduleEvent, которые оба сообщают о себе как неоскверненные относительно третье должно, для согласованности, сообщать о себе как о неприкосновенности относительно друг друга. С другой стороны, не было бы проблем с тем, чтобы ScheduledGongEvent реализовать IComparable<ScheduledGongEvent> принять GongVolume во внимание, а также EventTime или с помощью ScheduledPopupMessageEvent делать то же самое со своим параметром Message.

Поэтому полезно иметь такие вещи, как процедуры сортировки, использовать IComparable<T>, если он существует, но иметь возможность вернуться к IComparable, если IComparable<T> не существует. Однако проверка того, реализует ли класс IComparable<T>, и выбор подходящей реализации, однако, немного дороже. К счастью, как только тип определяется как реализация IComparable<T>, на него можно положиться, чтобы всегда иметь его; Аналогично, если тип не имеет такой реализации, он никогда не будет. Кроме того, если общий класс имеет какие-либо статические поля, каждая комбинация параметров типа даст другой класс с разными полями. Таким образом, первый раз Comparer<T>.Default запускается с определенным типом параметра T, он будет хранить процедуру Comparer, которую он возвращает в статическое поле. Если Comparer<T> снова запускается с тем же типом, он вернет ту же самую процедуру сравнения. Хотя может показаться странным иметь статический класс Comparer<T> только с одним методом, создание отдельного класса Comparer<T> для каждого типа T предоставляет место для хранения созданной процедуры сравнения.

Ответ 7

Comparer<T> содержит фактический метод сравнения. Его можно использовать, если вы хотите сравнить объект по-другому, чем в реализации IComparable.