Как реализовать интерфейс IComparable?

Я заполняю массив экземплярами класса:

BankAccount[] a;
. . .

a = new BankAccount[]
{
    new BankAccount("George Smith", 500m),
    new BankAccount("Sid Zimmerman", 300m)
};

Как только я заполню этот массив, я хотел бы отсортировать его по сумме баланса. Для этого я хотел бы иметь возможность проверить, сортируется ли каждый элемент с помощью IComparable.
Мне нужно сделать это с помощью интерфейсов. Пока у меня есть следующий код:

public interface IComparable
{
    decimal CompareTo(BankAccount obj);
}

Но я не уверен, что это правильное решение. Любой совет?


person Alex Gordon    schedule 15.11.2010    source источник


Ответы (6)


Вы не должны определять IComparable самостоятельно. Он уже определен. Вместо этого вам нужно реализовать IComparable в своем классе BankAccount.

Там, где вы определили class BankAccount, убедитесь, что он реализует интерфейс IComparable. Затем напишите BankAccount.CompareTo, чтобы сравнить балансовые суммы двух объектов.

public class BankAccount : IComparable<BankAccount>
{
    [...]

    public int CompareTo(BankAccount that)
    {
        if (this.Balance <  that.Balance) return -1;
        if (this.Balance == that.Balance) return 0;
        return 1;
    }
}

Изменить, чтобы показать решение Джеффри Л. Уитледжа из комментариев:

public class BankAccount : IComparable<BankAccount>
{
    [...]

    public int CompareTo(BankAccount that)
    {
        return this.Balance.CompareTo(that.Balance);
    }
}
person abelenky    schedule 15.11.2010
comment
извините, не могли бы вы привести пример того, как я бы это реализовал? - person Alex Gordon; 15.11.2010
comment
Мне нравится return this.Balance.CompareTo(that.Balance); - person Jeffrey L Whitledge; 15.11.2010
comment
@я девушка, я не понимаю, что вы имеете в виду. Возможно, вам не ясно, какую часть кода я заменял. Я напишу все это в комментарии, а затем: public class BankAccount : IComparable<BankAccount> { [...] int CompareTo(BankAccount that) { return this.Balance.CompareTo(that.Balance); } } Так понятнее? - person Jeffrey L Whitledge; 16.11.2010
comment
Другой способ: return Balance - that.Balance; - person fbiagi; 25.07.2014
comment
В 1-й версии должно быть this.Balance < that.Balance для сортировки по балансу по возрастанию. -1 = это меньше того, 0 = это равно тому, 1 = это больше того - person Keith; 29.07.2014
comment
@fbiagi Спасибо, что обратили на это внимание. Большинство людей думают, что IComparable всегда возвращает -1, 0 или 1, но в правилах нет ничего, что говорило бы, что IComparable вернет -1 или 1. Определение IComparable гласит, что он вернет число меньше нуля или число больше чем ноль, что означает, что для кого-то вполне приемлемо использовать вычитание для реализации CompareTo. Некоторые люди не знают об этом и начинают использовать операторы switch для IComparable.CompareTo, что неверно и может привести к поломке при определенных сравнениях. - person Pharap; 21.09.2014
comment
Вы также можете вернуть 1 для нулевого объекта that, если считаете, что указатель null имеет меньшую внутреннюю ценность. В приведенном выше примере выдается нуль, и я не уверен, что это желательно во время операций сортировки. - person Luke Puplett; 15.10.2014
comment
Реализация интерфейса не может быть общедоступной, если я правильно помню - person nicecatch; 12.01.2015
comment
Я считаю, что у вас есть ваши результаты в обратном направлении. Если текущее значение объекта БОЛЬШЕ, чем сравниваемое, то оно должно возвращать 1. У вас оно возвращает -1. - person Jeffrey Harmon; 28.08.2015
comment
return Balance - that.Balance не является хорошей идеей, если баланс когда-либо приближается к пределам своего типа, потому что переполнение может дать вам неправильные результаты. Например, если баланс равен short, this.Balance равен 32700, а that.Balance равен -100, результатом вычитания будет -32736, тогда как результат CompareTo должен быть положительным числом. Точно так же, если Баланс равен ushort, результат вычитания никогда не может быть отрицательным, что также явно неверно. - person James; 19.05.2016
comment
Я исправил ошибку в реализации первого примера — изменил > на <. Это первый результат Google для реализации icomparable, поэтому он, вероятно, должен быть правильным. - person neonblitzer; 08.10.2020

Вы хотите деструктивно отсортировать массив? То есть вы действительно хотите изменить порядок элементов в массиве? Или вам просто нужен список элементов в определенном порядке, не нарушая исходный порядок?

Я бы предположил, что почти всегда лучше сделать последнее. Рассмотрите возможность использования LINQ для неразрушающего упорядочения. (И рассмотрите возможность использования более значимого имени переменной, чем «a».)

BankAccount[] bankAccounts = { whatever };
var sortedByBalance = from bankAccount in bankAccounts 
                      orderby bankAccount.Balance 
                      select bankAccount;
Display(sortedByBalance);
person Eric Lippert    schedule 15.11.2010
comment
я хочу уничтожить его, реализуя icompare - person Alex Gordon; 15.11.2010
comment
@Lippert: Хотя это очень правильный ответ, из обсуждения кажется, что ОП едва понимает, что значит реализовать интерфейс. Возможно, она еще не готова к тому уровню вопросов, который вы задаете. - person abelenky; 15.11.2010
comment
Привет, Эрик, что лучше всего делать с null при реализации IComparable<T> и создании подклассов Comparer<T>, предполагая, что T является ссылочным типом? Зависит ли это от пользовательского варианта или обычно лучше генерировать исключение, поскольку реальная логика сравнения часто перенаправляется в какое-то свойство на T. - person stt106; 04.02.2017
comment
@ stt106: Звучит как вопрос; подумайте о том, чтобы опубликовать это как вопрос. Краткий ответ: я бы реализовал общий порядок для всех возможных значений, включая нуль. Традиционно null меньше, чем все другие возможности. Тем не менее, может быть разумно генерировать исключение, если вы считаете, что всегда неправильно указывать значение NULL. - person Eric Lippert; 05.02.2017

IComparable уже существует в .NET с этим определением CompareTo

int CompareTo(Object obj)

Вы не должны создавать интерфейс — вы должны его реализовать.

public class BankAccount : IComparable {

    int CompareTo(Object obj) {
           // return Less than zero if this object 
           // is less than the object specified by the CompareTo method.

           // return Zero if this object is equal to the object 
           // specified by the CompareTo method.

           // return Greater than zero if this object is greater than 
           // the object specified by the CompareTo method.
    }
}
person Lou Franco    schedule 15.11.2010
comment
извините, не могли бы вы привести пример того, как я бы это реализовал? - person Alex Gordon; 15.11.2010
comment
А что, если obj null или другого типа, чем BankAccount? РЕДАКТИРОВАТЬ: согласно MSDN здесь: msdn.microsoft.com/en-us/library/: введите ArgumentException. - person Ray; 21.04.2018

Альтернативой является использование LINQ и полное отсутствие реализации IComparable:

BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();
person Matt Greer    schedule 15.11.2010

Уже существует IComparable<T>, но в идеале вы должны поддерживать как IComparable<T>, так и IComparable. Использование встроенного Comparer<T>.Default обычно является более простым вариантом. Array.Sort, например, примет такой компаратор.

person Marc Gravell    schedule 15.11.2010

Если вам нужно только отсортировать эти BankAccounts, используйте LINQ, как показано ниже.

BankAccount[] a = new BankAccount[]
{
    new BankAccount("George Smith", 500m),
    new BankAccount("Sid Zimmerman", 300m)
};

a = a.OrderBy(bank => bank.Balance).ToArray();
person Zain Shaikh    schedule 15.11.2010