Як реалізувати інтерфейс IComparable?


78

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

BankAccount[] a;
. . .

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

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

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

Але я не впевнений, що це правильне рішення. Будь-яка порада?

Відповіді:


138

Ви не повинні визначати 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);
    }
}

42
Мені подобаєтьсяreturn this.Balance.CompareTo(that.Balance);
Jeffrey L Whitledge

3
@ я дівчина - я не впевнений, що ти маєш на увазі. Можливо, вам незрозуміло, яку частину коду я замінював. Я все це викладу в коментарі, тоді: public class BankAccount : IComparable<BankAccount> { [...] int CompareTo(BankAccount that) { return this.Balance.CompareTo(that.Balance); } }Чи це зрозуміліше?
Jeffrey L Whitledge

5
Інший спосібreturn Balance - that.Balance;
fbiagi

7
У 1-й версії це має бути this.Balance < that.Balanceсортування за зростанням за зростанням. -1 = це менше від цього, 0 = це дорівнює тому, 1 = це більше від цього
Кіт

5
return Balance - that.Balanceце не гарна ідея, якщо баланс коли-небудь наближається до меж свого типу, оскільки переповнення може дати вам неправильні результати. Наприклад, якби баланс був a short, і this.Balanceстановив 32700 і that.Balanceстановив -100, результатом віднімання буде -32736, коли однозначно результатом CompareToмає бути додатне число. Подібним чином, якщо баланс дорівнює a ushort, результат віднімання ніколи не може бути негативним, що також є явно неправильним.
Джеймс,

17

Ви хочете деструктивно відсортувати масив? Тобто, ви хочете насправді змінити порядок елементів у масиві? Або ви просто хочете отримати список елементів у певному порядку, не руйнуючи початковий порядок?

Я вважаю, що майже завжди краще робити останнє. Розгляньте можливість використання LINQ для неруйнівного впорядкування. (І розгляньте можливість використання більш значущої назви змінної, ніж "a".)

BankAccount[] bankAccounts = { whatever };
var sortedByBalance = from bankAccount in bankAccounts 
                      orderby bankAccount.Balance 
                      select bankAccount;
Display(sortedByBalance);

я хочу знищити його, впроваджуючи icompare
Алекс Гордон,

8
@Lippert: Незважаючи на те, що це дуже вагома відповідь, з обговорення видається, що OP ледве розуміє, що означає реалізація інтерфейсу. Можливо, вона ще не готова до рівня питань, які ви задаєте.
abelenky

Привіт Ерік, з якою найкращою практикою маємо справу nullпри впровадженні IComparable<T>та підкласуванні, Comparer<T>припускаючи T, що це посилальний тип? Це залежить від випадку користувача, або, як правило, краще викинути виняток, оскільки реальна логіка порівняння часто пересилається до певної властивості T.
stt106

1
@ stt106: Це звучить як запитання; розгляньте можливість розміщення цього питання. Коротка відповідь: я б реалізував загальний порядок для всіх можливих значень, включаючи null. Традиційно нуль менше, ніж усі інші можливості. Тим не менш, може бути розумно подати виняток, якщо ви вважаєте, що завжди неправильно надавати null.
Ерік Ліпперт,

15

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.
    }
}

1
мені шкода, що можете дати мені приклад того, як я це реалізував би
Алекс Гордон

2
А що, якщо obj є nullабо іншого типу, ніж BankAccount? EDIT: Відповідно до MSDN тут: msdn.microsoft.com/en-us/library/… : киньте ArgumentException.
Рей

11

Альтернативою є використання LINQ і пропуск реалізації IComparable взагалі:

BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();

6

Уже є IComparable<T>, але в ідеалі вам слід підтримати обидва IComparable<T>і IComparable. Використання вбудованого, Comparer<T>.Defaultяк правило, простіший варіант. Array.Sort, наприклад, прийме такого порівняльника.


2

Якщо вам потрібно лише відсортувати їх BankAccounts, використовуйте, LINQяк показано нижче

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

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