90

如何比较泛型类型的值?

我已将其减少到最小样本:

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),我们仍然不能应用任何运算符<><=>=或. (我知道涉及and的解决方法,但它对关系运算符没有帮助)。==!=Equals()==!=

所以,两个问题:

  1. 为什么我们会观察到这种奇怪的行为?是什么让我们无法比较已知的泛型类型的值IComparable?它不是以某种方式破坏了通用约束的全部目的吗?
  2. 我该如何解决这个问题,或者至少解决它?

(我意识到已经有一些与这个看似简单的问题相关的问题 - 但没有一个线程给出详尽或可行的答案,所以在这里。)

4

8 回答 8

101

IComparable不会使>=运算符超载。你应该使用

value.CompareTo(_minimumValue) >= 0
于 2011-06-25T21:18:18.203 回答
36

如果value可以为空,则当前答案可能会失败。改用这样的东西:

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
于 2014-11-07T15:18:07.557 回答
35

运算符重载问题

不幸的是,接口不能包含重载的运算符。尝试在你的编译器中输入这个:

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
于 2011-06-25T21:33:51.230 回答
7
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

使用 IComparable 泛型时,所有小于/大于运算符都需要转换为对 CompareTo 的调用。无论您使用什么运算符,都要保持以相同顺序比较的值,并与零进行比较。(x <op> y变成x.CompareTo(y) <op> 0, where <op>is >,>=等)

另外,我建议您使用的通用约束是where T : IComparable<T>. IComparable 本身意味着可以将对象与任何东西进行比较,将对象与相同类型的其他对象进行比较可能更合适。

于 2011-06-25T21:18:03.117 回答
3

而不是value >= _minimValue使用Comparer类:

public bool IsInRange(T value ) {
    var result = Comparer<T>.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}
于 2011-06-25T21:19:53.127 回答
2

正如其他人所说,需要明确使用 CompareTo 方法。不能将接口与运算符一起使用的原因是,一个类可以实现任意数量的接口,它们之间没有明确的排名。假设有人试图计算表达式“a = foo + 5;” 当 foo 实现了六个接口时,所有这些接口都定义了一个带有第二个整数参数的运算符“+”;操作员应该使用哪个接口?

类可以派生多个接口这一事实使得接口非常强大。不幸的是,它经常迫使人们更明确地了解自己真正想要做什么。

于 2011-06-25T21:58:31.553 回答
1

IComparable只强制调用一个函数CompareTo()。所以你不能应用你提到的任何运算符

于 2011-06-25T21:19:25.443 回答
0

我能够使用 Peter Hedburg 的答案为泛型创建一些重载的扩展方法。请注意,该CompareTo方法在这里不起作用,因为类型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

- 编辑 -

如OP所示,我能够通过限制所有方法T来清理它。IComparable(Of T)请注意,此约束也需要类型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
于 2017-12-14T06:42:17.477 回答