2009-05-27 14 views
9

Wenn ich will den Typ T auf Zwang vergleichbar zu sein, sollte ich verwenden:Schnittstelle Einschränkung für IComparable

where T : IComparable 

oder

where T : IComparable<T> 

Ich kann nicht meinen Kopf herum, wenn # 2 Marken Sinn. Jeder kann erklären, was der Unterschied wäre?

Antwort

2

Die IComparable<T> ermöglicht den Komparator stark typisiert.

können Sie

public int CompareTo(MyType other) 
{ 
    // logic 
} 

haben als zu

public int CompareTo(object other) 
{ 
    if (other is MyType) 
     // logic 
} 

zum Beispiel implementiert das nächste Beispiel Nehmen Sie opponieren Hexe beide Schnittstellen:

public class MyType : IComparable<MyType>, IComparable 
{ 
    public MyType(string name, int id) 
    { Name = name; Id = id; } 

    public string Name { get; set; } 
    public int Id { get; set; } 

    public int CompareTo(MyType other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     return (Id - other.Id > 0 ? 1 : 0); 
    } 

    public int CompareTo(object other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     if (other is MyType) 
      return (Id - (other as MyType).Id > 0 ? 1 : 0); 
     else 
      throw new InvalidOperationException("Bad type"); 
    } 
} 


MyType t1 = new MyType("a", 1); 
MyType t2 = new MyType("b", 2); 
object someObj = new object(); 

// calls the strongly typed method: CompareTo(MyType other) 
t1.CompareTo(t2); 
// calls the *weakly* typed method: CompareTo(object other) 
t1.CompareTo(someObj); 

Wenn MyType nur mit IComparable<MyType> umgesetzt wurde, Die zweite compareTo(someObj) ist eine Kompilierzeit Error. Dies ist ein Vorteil von stark typisierten Generika.

Auf der anderen Seite gibt es Methoden im Framework, die nicht generische IComparable wie Array.Sort erfordern. In diesen Fällen sollten Sie beide Schnittstellen wie in diesem Beispiel implementieren.

+1

bruno: In Ihrem Code würde ich eher den Vergleichsjob in CompareTo (Objekt) zu CompareTo (MyType) im zweiten Zweig von CompareTo (Objekt) delegieren, anstatt den Code zu duplizieren. – Steve

5

Der Hauptunterschied zwischen IComparable und IComparable <> ist, dass die erste so vorge generics ist ermöglicht das Vergleichsverfahren mit einem Objekt zu nennen, während die zweiten erzwingt, dass sie die gleiche Art teilen:

IComparable - CompareTo(object other); 
IComparable<T> - CompareTo(T other); 

Ich würde mit der zweiten Option gehen vorausgesetzt, dass Sie keine alten .net 1.0-Bibliotheken verwenden möchten, wo die Typen die moderne, generische Lösung möglicherweise nicht implementieren. Sie werden einen Leistungsschub bekommen, weil Sie Boxen vermeiden und die Vergleiche müssen nicht überprüfen, ob die Typen übereinstimmen, und Sie werden auch das warme Gefühl bekommen, das von Dingen auf modernste Art und Weise kommt ...

Um Jeffs sehr guten und sachdienlichen Punkt anzusprechen, würde ich argumentieren, dass es eine gute Übung ist, so wenig Einschränkungen für ein generisches wie für die Ausführung der Aufgabe erforderlich zu machen. Da Sie die vollständige Kontrolle über den Code innerhalb des generischen Elements haben, wissen Sie, ob Sie Methoden verwenden, die einen grundlegenden IComparable-Typ erfordern. Also, seinen Kommentar zu berücksichtigen persönlich würde ich diese Regeln befolgen:

  • Wenn Sie nicht die generische erwarten alle Typen zu verwenden, die nur IComparable implementieren (dh Legacy 1.0-Code) und Sie rufen nicht Methoden innerhalb des generischen Objekts, die auf einem IComparable-Parameter basieren, verwenden dann nur die IComparable-Bedingung <>.

  • Wenn Sie sind Typen verwenden, die nur IComparable implementieren dann nur diese Einschränkung verwenden

  • Wenn Sie Methoden verwenden, die einen IComparable Parameter erfordern, aber nicht Typen verwenden, die nur implementieren IComparable dann beide Einschränkungen verwenden als In Jeffs Antwort wird die Leistung gesteigert, wenn Sie Methoden verwenden, die den generischen Typ akzeptieren.

auf der dritten Regel erweitern - nehmen wir an, dass die Klasse, die Sie schreiben, ist wie folgt:

public class StrangeExample<T> where ... //to be decided 
{ 
    public void SortArray(T[] input) 
    { 
     Array.Sort(input); 
    } 

    public bool AreEqual(T a, T b) 
    { 
     return a.CompareTo(b) == 0; 
    } 
} 

Und wir müssen entscheiden, welche Abhängigkeiten zu platzieren darauf. Die SortArray-Methode ruft Array.Sort auf, das erfordert, dass das Array, das übergeben wird, Objekte enthält, die IComparable implementieren. Deshalb wir Muss haben eine IComparable Einschränkung:

public class StrangeExample<T> where T : IComparable 

Jetzt wird die Klasse kompiliert und korrekt funktioniert als ein Array von T gilt für Array.Sort ist und es eine gültige .CompareTo Methode in der Schnittstelle definiert. Wenn Sie jedoch sicher sind, dass Sie nicht mit einer Art verwenden, um Ihre Klasse wollen, die nicht auch die IComparable <> Schnittstelle nicht implementieren können Sie Ihre Einschränkung erweitern:

public class StrangeExample<T> where T : IComparable, IComparable<T> 

Das bedeutet, dass, wenn AreEqual sie genannt wird Verwenden Sie die schnellere, generische CompareTo-Methode, und Sie werden einen Leistungsvorteil auf Kosten der alten .NET 1.0-Typen feststellen.

Auf der anderen Seite, wenn Sie nicht die AreEqual-Methode haben, gibt es keinen Vorteil für die IComparable <> -Constraint, so dass Sie es auch löschen können - Sie verwenden sowieso nur IComparable-Implementierungen.

+1

Dies ist nicht ganz richtig, da einige Elemente der BCL nur mit IComparable und nicht IComparable wie Array.Sort und ArrayList.Sort arbeiten. Eigentlich sollte die Einschränkung beide Schnittstellen erzwingen. –

+0

Danke Martin. Kannst du bitte die letzte Regel erklären, die du geschrieben hast? Meinst du, Leistung wird besser, wenn ich beide Schnittstellen habe und den generischen Vergleich verwende? –

+0

Ja, da der Hauptvorteil der Verwendung der IComparable <> - Einschränkung ein Leistungsvorteil ist, wenn Sie den IComparable-Typ unterstützen müssen, da Sie Methoden verwenden, die dies erfordern (wie Array.Sort), die Sie jedoch * unterstützen können Der IComparable <> -Typ, da Sie keine Legacy-Typen verwenden, die ihn nicht implementieren, dann ist es am besten, beide zu benötigen und das Objekt als den schnelleren Typ zu verwenden, wo Sie können. –

1

Ich würde die zweite Einschränkung verwenden, da Sie stark typisierte Mitglieder der Schnittstelle referenzieren können. Wenn Sie mit Ihrer ersten Option fortfahren, müssen Sie eine Umwandlung durchführen, um den Schnittstellentyp zu verwenden.

2

Das sind zwei verschiedene Schnittstellen. Vor .NET 2.0 gab es keine Generika, also gab es nur IComparable. Mit .NET 2.0 kam Generics und es wurde möglich, IComparable<T> zu machen. Sie machen genau das Gleiche. Grundsätzlich ist IComparable veraltet, obwohl die meisten Bibliotheken da draußen beide erkennen.

Um Ihren Code wirklich kompatibel zu machen, implementieren Sie beide, aber rufen Sie den anderen an, damit Sie nicht denselben Code zweimal schreiben müssen.

5

Sie können beide Einschränkungen wollen, wie in:

where T : IComparable, IComparable<T> 

Dies würde Ihre Art kompatibel mit mehr Nutzer der IComparable Schnittstellen machen. Die generische Version von IComparable, IComparable<T> wird helfen, Boxen zu vermeiden, wenn T ein Werttyp ist und stärker typisierte Implementierungen der Schnittstellenmethoden ermöglicht. Wenn Sie beides unterstützen, wird sichergestellt, dass Ihr Objekt unabhängig davon, welche Schnittstelle ein anderes Objekt benötigt, konform ist und daher gut funktioniert.

Zum Beispiel Array.Sort und ArrayList.Sort verwenden Sie IComparable, nicht .