如何比较泛型类型的值?

发布于 2024-11-16 22:57:16 字数 919 浏览 1 评论 0原文

如何比较泛型类型的值?

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

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() 的解决方法,但它对关系运算符没有帮助)。

那么,有两个问题:

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

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

How do I compare values of generic types?

I have reduced it to a minimal sample:

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

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

The error is:

Operator '>=' cannot be applied to operands of type 'T' and 'T'.

What on earth!? T is already constrained to IComparable, and even when constraining it to value types (where T: struct), we still can't apply any of the operators <, >, <=, >=, == or !=. (I know that workarounds involving Equals() exist for == and !=, but it doesn't help for the relational operators).

So, two questions:

  1. Why do we observe this weird behaviour? What keeps us from comparing the values of generic types which are known to be IComparable? Doesn't it somehow defeat the entire purpose of generic constraints?
  2. How do I resolve this, or at least work around it?

(I realize there are already a handful of questions related to this seemingly simple problem - but none of the threads gives an exhaustive or workable answer, so here.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

嗫嚅 2024-11-23 22:57:16

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

value.CompareTo(_minimumValue) >= 0

IComparable doesn't overload the >= operator. You should use

value.CompareTo(_minimumValue) >= 0
幽梦紫曦~ 2024-11-23 22:57:16

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

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

If value can be null the current answer could fail. Use something like this instead:

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
北凤男飞 2024-11-23 22:57:16

运算符重载问题

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

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

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

要么是这样,要么是设计师不喜欢滥用的可能性。例如,想象一下对类 MagicMrMeow 进行 >= 比较。或者甚至在类 Matrix 上。这两个值的结果意味着什么?尤其是当可能存在歧义时?

官方解决方法

由于上述接口不合法,我们有 IComparable 接口来解决该问题。它不实现任何运算符,并且仅公开一种方法,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.

使用解决方法

为了执行与 value >= _minimumValue 相同的操作,您必须改为编写:

value.CompareTo(_minimumValue) >= 0

Problem with operator overloading

Unfortunately, interfaces cannot contain overloaded operators. Try typing this in your compiler:

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

I don't know why they didn't allow this, but I'm guessing it complicated the language definition, and would be hard for users to implement correctly.

Either that, or the designers didn't like the potential for abuse. For example, imagine doing a >= compare on a class MagicMrMeow. Or even on a class Matrix<T>. What does the result mean about the two values?; Especially when there could be an ambiguity?

The official work-around

Since the above interface isn't legal, we have the IComparable<T> interface to work around the problem. It implements no operators, and exposes only one method, int CompareTo(T other);

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

The int result is actually a tri-bit, or a tri-nary (similar to a Boolean, but with three states). This table explains the meaning of the results:

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.

Using the work-around

In order to do the equivalent of value >= _minimumValue, you must instead write:

value.CompareTo(_minimumValue) >= 0
花开柳相依 2024-11-23 22:57:16
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

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

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

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

When working with IComparable generics, all less than/greater than operators need to be converted to calls to CompareTo. Whatever operator you would use, keep the values being compared in the same order, and compare against zero. ( x <op> y becomes x.CompareTo(y) <op> 0, where <op> is >, >=, etc.)

Also, I'd recommend that the generic constraint you use be where T : IComparable<T>. IComparable by itself means that the object can be compared against anything, comparing an object against others of the same type is probably more appropriate.

愛上了 2024-11-23 22:57:16

使用 Comparer 类代替 value >= _minimValue

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

Instead of value >= _minimValue use Comparer class:

public bool IsInRange(T value ) {
    var result = Comparer<T>.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}
蓬勃野心 2024-11-23 22:57:16

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

类可以派生多个接口这一事实使得接口非常强大。不幸的是,它常常迫使人们更加明确地表达自己真正想做的事情。

As others have stated, one needs to explicitly use the CompareTo method. The reason that one cannot use interfaces with operators is that it is possible for a class to implement an arbitrary number of interfaces, with no clear ranking among them. Suppose one tried to compute the expression "a = foo + 5;" when foo implemented six interfaces all of which define an operator "+" with an integer second argument; which interface should be used for the operator?

The fact that classes can derive multiple interfaces makes interfaces very powerful. Unfortunately, it often forces one to be more explicit about what one actually wants to do.

初见你 2024-11-23 22:57:16

IComparable 仅强制使用名为 CompareTo() 的函数。所以你不能应用你提到的任何运算符

IComparable only forces a function called CompareTo(). So you cannot apply any of the operators that you have mentioned

寂寞花火° 2024-11-23 22:57:16

我能够使用 Peter Hedberg 的答案为泛型创建一些重载的扩展方法。请注意,CompareTo 方法在这里不起作用,因为类型 T 未知并且不提供该接口。也就是说,我有兴趣看到任何替代方案。

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance)
{
  Instance.RemoveDuplicates((X, Y) => Comparer<T>.Default.Compare(X, Y));
}

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance, Comparison<T> Comparison)
{
  Instance.RemoveDuplicates(new List<Comparison<T>>() { Comparison });
}

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance, List<Comparison<T>> Comparisons)
{
  List<bool> oResults = new List<bool>();

  for (int i = 0; i <= Instance.Count - 1; i++)
  {
    for (int j = Instance.Count - 1; j >= i + 1; j += -1)
    {
      oResults.Clear();

      foreach (Comparison<T> oComparison in Comparisons)
        oResults.Add(oComparison(Instance[i], Instance[j]) == 0);

      if (oResults.Any(R => R))
        Instance.RemoveAt(j);
    }
  }
}

--编辑--

我能够通过将所有方法上的 T 限制为 IComparable(Of T) 来清理这个问题,如 OP 所示。请注意,此约束也需要类型 T 来实现 IComparable(Of)

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance) where T : IComparable<T>
{
    Instance.RemoveDuplicates((X, Y) => X.CompareTo(Y));
}

I was able to use Peter Hedberg's answer to create some overloaded extension methods for generics. Note that the CompareTo method doesn't work here, as type T is unknown and doesn't present that interface. That said, I'm interested in seeing any alternatives.

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance)
{
  Instance.RemoveDuplicates((X, Y) => Comparer<T>.Default.Compare(X, Y));
}

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance, Comparison<T> Comparison)
{
  Instance.RemoveDuplicates(new List<Comparison<T>>() { Comparison });
}

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance, List<Comparison<T>> Comparisons)
{
  List<bool> oResults = new List<bool>();

  for (int i = 0; i <= Instance.Count - 1; i++)
  {
    for (int j = Instance.Count - 1; j >= i + 1; j += -1)
    {
      oResults.Clear();

      foreach (Comparison<T> oComparison in Comparisons)
        oResults.Add(oComparison(Instance[i], Instance[j]) == 0);

      if (oResults.Any(R => R))
        Instance.RemoveAt(j);
    }
  }
}

--EDIT--

I was able to clean this up by constraining T to IComparable(Of T) on all methods, as indicated by OP. Note that this constraint requires type T to implement IComparable(Of <type>) as well.

[DebuggerStepThrough]
public void RemoveDuplicates<T>(this List<T> Instance) where T : IComparable<T>
{
    Instance.RemoveDuplicates((X, Y) => X.CompareTo(Y));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文