Подтвердить что ты не робот

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

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

Я уменьшил его до минимального образца:

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), мы по-прежнему не можем применять ни один из операторов <, >, <=, >=, == или !=. (Я знаю, что обходные пути с участием Equals() существуют для == и !=, но это не помогает для реляционных операторов).

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

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

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

4b9b3361

Ответ 1

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

value.CompareTo(_minimumValue) >= 0

Ответ 2

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

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

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 на самом деле является трехбитным или трехзначным (похожим на a 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

Ответ 3

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

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

Ответ 4

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

При работе с генерируемыми IComparable все меньше или больше операторов необходимо преобразовать в вызовы CompareTo. Независимо от того, какой оператор вы будете использовать, сохраните значения, сравниваемые в том же порядке, и сравните их с нулем. (x <op> y становится x.CompareTo(y) <op> 0, где <op> - >, >= и т.д.)

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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