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

IComparable и IComparable <T>

Должен ли я реализовать как IComparable, так и общий IComparable<T>? Существуют ли какие-либо ограничения, если я реализую только один из них?

4b9b3361

Ответ 1

Да, вы должны реализовать оба.

Если вы его реализуете, любой код, который зависит от другого, не будет работать.

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

Ответ 2

Oded прав, что вы должны реализовать оба, потому что есть коллекции и другие классы, которые полагаются только на одну из реализаций.

Но есть трюк: IComparable <T> не следует бросать исключения, в то время как IComparable должен. При реализации IComparable <T> вы отвечаете за то, чтобы все экземпляры T могли сравниваться друг с другом. Это также включает в себя null (обрабатывать значение null как меньше, чем все ненулевые экземпляры T, и все будет хорошо).

Однако общий IComparable принимает System.Object, и вы не можете гарантировать, что все мыслимые объекты будут сопоставимы с экземплярами T. Поэтому, если вы получите экземпляр non-T, переданный в IComparable, просто выбросите System.ArgumentException. В противном случае проведите вызов к IComparable <T> реализация.

Вот пример:

public class Piano : IComparable<Piano>, IComparable
{
    public int CompareTo(Piano other) { ... }
    ...
    public int CompareTo(object obj)
    {

        if (obj != null && !(obj is Piano))
            throw new ArgumentException("Object must be of type Piano.");

        return CompareTo(obj as Piano);

    }
}

Этот пример является частью гораздо более длинной статьи, которая содержит обширный анализ побочных эффектов, которые вы должны соблюдать при реализации IComparable <T> : Как сделать Внедрить IComparable <T> Интерфейс в базовых и производных классах

Ответ 3

В то время как IEquatable <T> как правило, не реализуются незапечатанными классами, поскольку такой вывод будет играть странно с наследованием, если только реализация просто не вызовет Object.Equals(в этом случае это будет бессмысленно), возникает противоположная ситуация с общим IComparable <T> . Семантика для Object.Equals и IEquatable <T> подразумевают, что всякий раз, когда IEquatable <T> определяется, его поведение должно отражать поведение Object.Equals(кроме, возможно, более быстрого и избегающего бокса). Два объекта типа DerivedFoo, которые сравниваются как равные, если их рассматривать как тип DerivedFoo, также должны сравнивать равные, если рассматривать их как объекты типа Foo, и наоборот. С другой стороны, вполне возможно, что два объекта типа DerivedFoo, которые ранжируются неравномерно, если их рассматривать как тип DerivedFoo, должны одинаково оцениваться, если рассматривать их как тип Foo. Единственный способ убедиться в этом - использовать IComparable <T> .

Предположим, например, что у вас есть класс SchedulerEvent, который содержит поля ScheduledTime (типа DateTime) и ScheduledAction (типа MethodInvoker). Класс включает подтипы SchedulerEventWithMessage (который добавляет поле Message строки типа) и SchedulerEventWithGong (который добавляет поле GongVolume типа Double). Класс SchedulerEvent имеет естественное упорядочение по ScheduledTime, но вполне возможно, что события, которые неупорядочены относительно друг друга, неравны. Классы SchedulerEventWithMessage и SchedulerEventWithGong также имеют естественный порядок между собой, но не по сравнению с элементами класса SchedulerEvent.

Предположим, что для одного и того же времени запланировано два события SchedulerEventWithMessage X и Y, но X.Message - это "aardvark" и Y.Message - "zymurgy". ((IComparable <SchedulerEvent> ) X).CompareTo(Y) должен сообщать о нуле (поскольку события имеют одинаковое время), но ((IComparable <SchedulerEventWithMessage> ) X).CompareTo(Y) должен возвращать отрицательное число (поскольку "aardvark" до "зимургии" ). Если класс не вел себя так, было бы сложно или невозможно последовательно упорядочить список, содержащий смесь объектов SchedulerEventWithMessage и SchedulerEventWithGong.

Кстати, можно утверждать, что было бы полезно иметь семантику IEquatable <T> сравнить объекты только с базой элементов типа T, так что, например, IEquatable <SchedulerEvent> будет проверять ScheduledTime и ScheduledAction на равенство, но даже если применить к SchedulerEventWithMessage или SchedulerEventWithGong не будет проверять свойства Message или GongVolume. Действительно, это будет полезной семантикой для IEquatable <T> метод, и я бы предпочел бы такую ​​семантику, но для одной проблемы: Comparer <T> .Default.GetHashCode(T) всегда вызывает ту же функцию Object.GetHashCode(), независимо от типа T. Это значительно ограничивает способность IEquatable <T> для изменения своего поведения с разными типами T.