如何比较泛型类型的值?

gst*_*ken 70 .net c# generics icomparable

如何比较泛型类型的值?

我把它减少到最小的样本:

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

    public bool IsInRange(T value) 
    {
        return (value >= _minimumValue); // <-- Error here
    }
}
Run Code Online (Sandbox Code Playgroud)

错误是:

运算符'> ='不能应用于'T'和'T'类型的操作数.

到底怎么回事!?T已被约束到IComparable,甚至它限制值类型(的时候where T: struct),我们仍然不能将任何运营商<,>,<=,>=,==!=.(我知道,涉及到解决方法Equals()的存在==!=,但它并不能帮助对于关系运算符).

那么,有两个问题:

  1. 为什么我们观察到这种奇怪的行为?是什么让我们从比较这是泛型类型的值称为IComparable?它不是以某种方式打败了通用约束的整个目的吗?
  2. 我该如何解决这个问题,或至少解决这个问题?

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

fae*_*ter 87

IComparable不会使>=操作员超载.你应该用

value.CompareTo(_minimumValue) >= 0
Run Code Online (Sandbox Code Playgroud)

  • 太好了,这个工作(当然也解释了) - 非常感谢!但它有点不满意,并留下了一个问题:*为什么*IComparable不会使比较运算符超载?这是一个有意识和深思熟虑的设计决策,有充分的理由 - 或者在框架设计中被忽略的东西?毕竟,'x.CompareTo(y)> = 0'的可读性低于'x> = y',不是吗? (5认同)
  • @gstercken:"IComparable"重载比较运算符的一个问题是,有些情况下`X.Equals(Y)`应该返回false,但是`X.CompareTo(Y)`应该返回零(表明这两个元素都不大于另一个)[例如`'ExpenseItem`可能具有相对于'TotalCost`的自然顺序,并且对于成本相同的费用项目可能没有自然排序,但这并不意味着每个花费$ 3,141.59的费用项目应该是被认为相当于其他每个成本相同的项目. (5认同)
  • @gstercken:从根本上讲,“==”在逻辑上可能意味着很多东西。在某些情况下,“X==Y”可能为 true,而“X.Equals(Y)”为 false,而在其他情况下,“X==Y”可能为 false,而“X.Equals(Y)” ` 是真的。即使可以为接口重载运算符,根据“IComparable&lt;T&gt;”重载“&lt;”、“&lt;=”、“&gt;”和“&gt;=”可能会给人一种“==”和“!=”的印象在这种情况下也会超载。如果 C# 像 vb 一样,不允许在未重载的类类型上使用“==”,那可能还不错,但是…… (2认同)
  • ...唉,C#决定使用令牌`==`来表示可重载的等于运算符和不可重载的引用相等性测试. (2认同)

Mer*_*ham 31

运算符重载问题

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

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);
}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么他们不允许这样做,但我猜它使语言定义变得复杂,并且用户很难正确实现.

要不然,或者设计师不喜欢滥用的可能性.例如,想象一下做一个>=比较class MagicMrMeow.甚至是一个class Matrix<T>.结果对这两个值意味着什么?特别是当可能存在歧义时?

官方解决方案

由于上述界面不合法,我们有IComparable<T>界面来解决问题.它不实现任何运算符,只暴露一个方法,int CompareTo(T other);

请参阅http://msdn.microsoft.com/en-us/library/4d7sx9hd.aspx

int结果实际上是一个三比特,或三进制(类似于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.
Run Code Online (Sandbox Code Playgroud)

使用解决方法

为了做相同的事情value >= _minimumValue,你必须写:

value.CompareTo(_minimumValue) >= 0
Run Code Online (Sandbox Code Playgroud)

  • 啊,对 - 有道理。我忘记了 C# 中的接口不能重载运算符。 (2认同)

Pet*_*erg 25

如果value可以为null,则当前答案可能会失败.使用这样的东西代替:

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
Run Code Online (Sandbox Code Playgroud)


Dav*_*Yaw 6

public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}
Run Code Online (Sandbox Code Playgroud)

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

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