Как сравнить значения универсальных типов?

как сравнить значения общих типов?

я сократил его до минимального образца:

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

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

ошибка:

оператор '> = ' не может быть применен к операндам типа 'T' и 'T'.

что за черт!? T уже ограничен IComparable, и даже при ограничении его типы значений (where T: struct), мы все еще не можем применить любой из операторов <, >, <=, >=, == или !=. (Я знаю, что обходные пути с участием на == и !=, но это не помогает для реляционных операторов).

Итак, два вопроса:

  1. почему мы наблюдаем это странное поведение? Что мешает нам сравнивать значения общих типов, которые являются известный на IComparable? Разве это каким-то образом не уничтожает всю цель общих ограничений?
  2. как мне решить эту проблему, или по крайней мере работать вокруг него?

(я понимаю, что уже есть несколько вопросов, связанных с этой, казалось бы, простой проблемой, но ни один из потоков не дает исчерпывающего или работоспособного ответа, поэтому здесь.)

8 ответов


IComparable не перегружает >= оператора. Вы должны использовать

value.CompareTo(_minimumValue) >= 0

проблема с перегрузкой оператора

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

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

Я не знаю, почему они не разрешили это, но я предполагаю, что это усложнило определение языка и было бы трудно для пользователей правильно реализовать.

либо это, либо дизайнерам не понравился потенциал для злоупотреблений. Например, представьте, что вы делаете >= сравнить на class MagicMrMeow. Или даже на class Matrix<T>. Что означает результат для этих двух значений? Особенно, когда может быть двусмысленность?

официальная работа-вокруг

поскольку вышеуказанный интерфейс не является законным, у нас есть IComparable<T> интерфейс для работы вокруг этой проблемы. Он не реализует никаких операторов и предоставляет только один метод, int CompareTo(T other);

см.http://msdn.microsoft.com/en-us/library/4d7sx9hd.aspx

в int результат на самом деле три-бит, или три-nary (подобно Boolean, но с тремя состояниями). Эта таблица объясняет значение результатов:

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.

используя обходной

для того, чтобы сделать эквивалент value >= _minimumValue, вы должны вместо этого написать:

value.CompareTo(_minimumValue) >= 0

Если value может быть null текущий ответ может завершиться ошибкой. Использовать что-то вроде этого:

Comparer<T>.Default.Compare(value, _minimumValue) >= 0

public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

при работе с icomparable дженериками все меньше / больше операторов должны быть преобразованы в вызовы CompareTo. Какой бы оператор вы ни использовали, держите значения сравниваемыми в том же порядке и сравнивайте с нулем. ( x <op> y становится x.CompareTo(y) <op> 0, где <op> is >, >=, etc.)

кроме того, я бы рекомендовал, чтобы общее ограничение, которое вы используете, было where T : IComparable<T>. IComparable само по себе означает, что объект можно сравнить с чем угодно, сравнивая объект против других такого же типа, вероятно, более уместен.


вместо value >= _minimValue использовать Comparer класс:

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

Как заявили другие, необходимо явно использовать метод CompareTo. Причина, по которой нельзя использовать интерфейсы с операторами, заключается в том, что класс может реализовать произвольное количество интерфейсов без четкого ранжирования между ними. Предположим, кто-то попытался вычислить выражение "a = foo + 5;", когда foo реализовал шесть интерфейсов, все из которых определяют оператор "+" с целочисленным вторым аргументом; какой интерфейс должен использоваться для оператора?

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


IComparable только заставляет функциюCompareTo(). Таким образом, вы не можете применить ни один из операторов, которые вы упомянули


Я был в состоянии использовать ответ Петр Hedburg создать несколько перегруженных методов расширения для дженериков. Обратите внимание, что CompareTo метод здесь не работает, так как type T неизвестно и не представляет этот интерфейс. Тем не менее, я заинтересован в том, чтобы увидеть любые альтернативы.

Я хотел бы опубликовать на C#, но конвертер Telerik не работает на этом коде. Я недостаточно знаком с C#, чтобы надежно конвертировать его вручную. Если кто-то хотел бы сделать честь, я был бы рад видеть это отредактировано соответствующим образом.

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

я смог очистить это, ограничивая T to IComparable(Of T) по всем методам, как указано в OP. Обратите внимание, что для этого ограничения требуется тип T для реализации IComparable(Of <type>) как хорошо.

<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