Wie implementiere ich eine IComparable-Schnittstelle?

76

Ich fülle ein Array mit Instanzen einer Klasse:

BankAccount[] a;
. . .

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

Sobald ich dieses Array ausgefüllt habe, möchte ich es nach Saldobeträgen sortieren. Dazu möchte ich prüfen können, ob jedes Element mit sortierbar ist IComparable.
Ich muss dies über Schnittstellen tun. Bisher habe ich folgenden Code:

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

Ich bin mir aber nicht sicher, ob dies die richtige Lösung ist. Irgendein Rat?

Alex Gordon
quelle

Antworten:

135

Sie sollten sich nicht definieren IComparable. Es ist bereits definiert. Vielmehr müssen Sie in Ihrer Klasse implementieren .IComparableBankAccount

Stellen Sie class BankAccountsicher, dass die IComparableSchnittstelle dort implementiert ist, wo Sie sie definiert haben . Schreiben Sie dann BankAccount.CompareTo, um die Restbeträge der beiden Objekte zu vergleichen.

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

Bearbeiten , um die Lösung von Jeffrey L Whitledge anhand von Kommentaren anzuzeigen:

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

    public int CompareTo(BankAccount that)
    {
        return this.Balance.CompareTo(that.Balance);
    }
}
abelenky
quelle
42
Ich magreturn this.Balance.CompareTo(that.Balance);
Jeffrey L Whitledge
3
Ich bin ein Mädchen - ich bin nicht sicher, was du meinst. Vielleicht ist Ihnen nicht klar, welchen Teil des Codes ich ersetzt habe. Ich werde dann alles in den Kommentar einfügen: public class BankAccount : IComparable<BankAccount> { [...] int CompareTo(BankAccount that) { return this.Balance.CompareTo(that.Balance); } }Ist das klarer?
Jeffrey L Whitledge
5
Ein anderer Weg wärereturn Balance - that.Balance;
fbiagi
7
In der 1. Version sollte es sein, this.Balance < that.Balancenach aufsteigendem Saldo zu sortieren. -1 = das ist weniger als das, 0 = das ist gleich das, 1 = das ist größer als das
Keith
5
return Balance - that.Balanceist keine gute Idee, wenn sich die Waage jemals den Grenzen ihres Typs nähert, da ein Überlauf zu falschen Ergebnissen führen kann. Wenn beispielsweise Balance a wäre shortund this.Balance32700 und that.Balance-100 wäre, wäre das Ergebnis der Subtraktion -32736, wenn das Ergebnis CompareToeindeutig eine positive Zahl sein sollte. In ähnlicher Weise kann ushortdas Ergebnis der Subtraktion niemals negativ sein , wenn Balance a ist , was ebenfalls eindeutig falsch ist.
James
16

Möchten Sie das Array destruktiv sortieren? Das heißt, möchten Sie die Reihenfolge der Elemente im Array tatsächlich ändern? Oder möchten Sie nur eine Liste der Artikel in einer bestimmten Reihenfolge, ohne die ursprüngliche Reihenfolge zu zerstören?

Ich würde vorschlagen, dass es fast immer besser ist, Letzteres zu tun. Erwägen Sie die Verwendung von LINQ für eine zerstörungsfreie Bestellung. (Und erwägen Sie die Verwendung eines aussagekräftigeren Variablennamens als "a".)

BankAccount[] bankAccounts = { whatever };
var sortedByBalance = from bankAccount in bankAccounts 
                      orderby bankAccount.Balance 
                      select bankAccount;
Display(sortedByBalance);
Eric Lippert
quelle
Ich möchte es zerstören, indem ich icompare implementiere
Alex Gordon
8
@Lippert: Obwohl dies eine sehr gültige Antwort ist, scheint es aus der Diskussion, dass das OP kaum versteht, was es bedeutet, eine Schnittstelle zu implementieren. Sie ist wahrscheinlich noch nicht bereit für die Fragen, die Sie stellen.
Abelenky
Hallo Eric, was ist die beste Vorgehensweise nullbeim Implementieren IComparable<T>und Unterklassen unter der Comparer<T>Annahme, dass Tes sich um einen Referenztyp handelt? Hängt es vom Benutzerfall ab oder ist es normalerweise besser, eine Ausnahme auszulösen, da die reale Vergleichslogik häufig an eine Eigenschaft weitergeleitet wird T.
stt106
1
@ stt106: Das klingt nach einer Frage; Erwägen Sie, es als Frage zu veröffentlichen. Kurze Antwort: Ich würde eine Gesamtreihenfolge für alle möglichen Werte implementieren, einschließlich null. Traditionell ist null kleiner als alle anderen Möglichkeiten. Es kann jedoch sinnvoll sein, eine Ausnahme auszulösen, wenn Sie der Meinung sind, dass die Angabe einer Null immer falsch ist .
Eric Lippert
15

IComparable existiert bereits in .NET mit dieser Definition von CompareTo

int CompareTo(Object obj)

Sie sollen die Schnittstelle nicht erstellen - Sie sollen sie implementieren.

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.
    }
}
Lou Franco
quelle
1
Es tut mir leid, können Sie mir ein Beispiel geben, wie ich es implementieren würde
Alex Gordon
2
Und was ist, wenn obj ist nulloder von einem anderen Typ als BankAccount? EDIT: Laut MSDN hier: msdn.microsoft.com/en-us/library/… : werfen Sie eine ArgumentException.
Ray
10

Eine Alternative besteht darin, LINQ zu verwenden und die Implementierung von IComparable insgesamt zu überspringen:

BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();
Matt Greer
quelle
5

Es gibt bereits IComparable<T>, aber Sie sollten idealerweise beide IComparable<T>und unterstützen IComparable. Die Verwendung des eingebauten Comparer<T>.Defaultist im Allgemeinen eine einfachere Option. Array.Sortakzeptiert beispielsweise einen solchen Vergleicher.

Marc Gravell
quelle
2

Wenn Sie diese nur sortieren müssen BankAccounts, verwenden Sie LINQFolgendes

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

a = a.OrderBy(bank => bank.Balance).ToArray();
Zain Shaikh
quelle