it-swarm.com.de

Wie kann ich Werte generischer Typen vergleichen?

Wie vergleiche ich Werte generischer Typen?

Ich habe es auf ein Minimum reduziert:

public class Foo<T> where T : IComparable
{
    private T _minimumValue = default(T);

    public bool IsInRange(T value) 
    {
        return (value >= _minimumValue); // <-- Error here
    }
}

Der Fehler ist:

Der Operator '> =' kann nicht auf Operanden vom Typ 'T' und 'T' angewendet werden.

Was in aller Welt!? T ist bereits auf IComparable beschränkt. Selbst wenn wir sie auf Werttypen (where T: struct) beschränken, können wir immer noch keinen der Operatoren <, >, <=, >=, == oder != anwenden. (Ich weiß, dass für == und != Workarounds mit Equals() vorhanden sind, die relationalen Operatoren jedoch nicht helfen.).

Also zwei Fragen:

  1. Warum beobachten wir dieses komische Verhalten? Was hindert uns daran, die Werte generischer Typen zu vergleichen, bei denen knownIComparable ist? Besiegt es nicht irgendwie den gesamten Zweck generischer Einschränkungen?
  2. Wie kann ich das beheben oder zumindest umgehen?

(Ich weiß, es gibt bereits eine Handvoll Fragen zu diesem scheinbar einfachen Problem - aber keiner der Threads gibt eine erschöpfende oder praktikable Antwort, also hier.)

66
gstercken

IComparable überlastet den >=-Operator nicht. Du solltest benutzen

value.CompareTo(_minimumValue) >= 0
83
faester

Problem mit dem Überladen des Bedieners

Schnittstellen können leider keine überladenen Operatoren enthalten. Versuchen Sie, dies in Ihrem Compiler einzugeben:

public interface IInequalityComaparable<T>
{
    bool operator >(T lhs, T rhs);
    bool operator >=(T lhs, T rhs);
    bool operator <(T lhs, T rhs);
    bool operator <=(T lhs, T rhs);
}

Ich weiß nicht, warum sie das nicht zugelassen haben, aber ich denke, es hat die Sprachdefinition kompliziert gemacht, und es wäre schwierig für Benutzer, sie korrekt zu implementieren.

Entweder das oder die Designer mochten das Missbrauchspotenzial nicht. Stellen Sie sich beispielsweise vor, dass Sie einen >=-Vergleich mit einem class MagicMrMeow durchführen. Oder sogar auf einen class Matrix<T>. Was bedeutet das Ergebnis für die beiden Werte ?; Vor allem, wenn es Unklarheiten geben könnte?

Die offizielle Lösung

Da die obige Schnittstelle nicht zulässig ist, haben wir die IComparable<T>-Schnittstelle, um das Problem zu umgehen. Es implementiert keine Operatoren und stellt nur eine Methode zur Verfügung, int CompareTo(T other);

Siehe http://msdn.Microsoft.com/de-de/library/4d7sx9hd.aspx

Das int-Ergebnis ist eigentlich ein Tri-Bit oder ein Tri-nary (ähnlich einer Boolean, aber mit drei Zuständen). Diese Tabelle erklärt die Bedeutung der Ergebnisse:

Value              Meaning

Less than zero     This object is less than
                   the object specified by the CompareTo method.

Zero               This object is equal to the method parameter.

Greater than zero  This object is greater than the method parameter.

Verwenden der Problemumgehung

Um das Äquivalent von value >= _minimumValue zu tun, müssen Sie stattdessen Folgendes schreiben:

value.CompareTo(_minimumValue) >= 0
29

Wenn value null sein kann, schlägt die aktuelle Antwort möglicherweise fehl. Verwenden Sie stattdessen so etwas:

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
18
Peter Hedberg
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

Wenn Sie mit IComparable-Generics arbeiten, müssen alle Operatoren, die kleiner oder größer als Operatoren sind, in Aufrufe von CompareTo konvertiert werden. Unabhängig davon, welchen Operator Sie verwenden würden, halten Sie die verglichenen Werte in derselben Reihenfolge und vergleichen Sie sie mit Null. (x <op> y wird zu x.CompareTo(y) <op> 0, wobei <op>>, >= usw. ist.)

Ich würde auch empfehlen, dass die generische Einschränkung, die Sie verwenden, where T : IComparable<T> ist. IComparable bedeutet für sich, dass das Objekt mit allem verglichen werden kann. Ein Vergleich mit anderen Objekten desselben Typs ist wahrscheinlich sinnvoller.

6
David Yaw

Anstelle von value >= _minimValue verwenden Sie Comparer class:

public bool IsInRange(T value ) {
    var result = Comparer<T>.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}
3
TcKs

Wie bereits erwähnt, muss explizit die CompareTo-Methode verwendet werden. Schnittstellen mit Operatoren können nicht verwendet werden, weil eine Klasse eine beliebige Anzahl von Schnittstellen implementieren kann, ohne dass eine klare Rangfolge vorliegt. Angenommen, man hat versucht, den Ausdruck "a = foo + 5;" zu berechnen. Wenn foo sechs Schnittstellen implementiert, definieren alle einen Operator "+" mit einem ganzzahligen zweiten Argument. Welche Schnittstelle soll für den Operator verwendet werden?

Die Tatsache, dass Klassen mehrere Schnittstellen ableiten können, macht Schnittstellen sehr leistungsfähig. Leider zwingt es oft dazu, genauer zu sagen, was man eigentlich tun will.

2
supercat

IComparable erzwingt nur eine Funktion namens CompareTo(). Sie können also keinen der von Ihnen genannten Operatoren anwenden

1

Ich konnte die Antwort von Peter Hedburg verwenden, um einige überlastete Erweiterungsmethoden für Generika zu erstellen. Beachten Sie, dass die CompareTo-Methode hier nicht funktioniert, da der Typ T unbekannt ist und dieses Interface nicht darstellt. Das heißt, ich bin an Alternativen interessiert.

Ich hätte gerne in C # gepostet, aber der Konverter von Telerik schlägt mit diesem Code fehl. Ich bin nicht vertraut mit C #, um es zuverlässig manuell zu konvertieren. Wenn jemand die Ehrungen machen möchte, würde ich mich freuen, wenn dies entsprechend bearbeitet wird.

<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T))
  Instance.RemoveDuplicates(Function(X, Y) Comparer(Of T).Default.Compare(X, Y))
End Sub



<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T), Comparison As Comparison(Of T))
  Instance.RemoveDuplicates(New List(Of Comparison(Of T)) From {Comparison})
End Sub



<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T), Comparisons As List(Of Comparison(Of T)))
  Dim oResults As New List(Of Boolean)

  For i As Integer = 0 To Instance.Count - 1
    For j As Integer = Instance.Count - 1 To i + 1 Step -1
      oResults.Clear()

      For Each oComparison As Comparison(Of T) In Comparisons
        oResults.Add(oComparison(Instance(i), Instance(j)) = 0)
      Next oComparison

      If oResults.Any(Function(R) R) Then
        Instance.RemoveAt(j)
      End If
    Next j
  Next i
End Sub

--EDIT--

Ich konnte dies bereinigen, indem ich T auf IComparable(Of T) für alle Methoden einschränkte, wie von OP angezeigt. Beachten Sie, dass diese Einschränkung den Typ T erfordert, um auch IComparable(Of <type>) zu implementieren.

<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T As IComparable(Of T))(Instance As List(Of T))
  Instance.RemoveDuplicates(Function(X, Y) X.CompareTo(Y))
End Sub
0
InteXX