11

当我想限制类型 T 具有可比性时,我应该使用:

where T : IComparable

或者

where T : IComparable<T>

如果#2有意义,我无法理解。任何人都可以解释有什么区别?

4

5 回答 5

6

IComparable 和 IComparable<> 之间的主要区别在于第一个是预泛型,因此允许您使用任何对象调用 compare 方法,而第二个强制它共享相同的类型:

IComparable - CompareTo(object other);
IComparable<T> - CompareTo(T other);

如果您不打算使用任何类型可能无法实现现代通用解决方案的旧 .net 1.0 库,我会选择第二个选项。您将获得性能提升,因为您将避免拳击,并且比较不需要检查类型匹配,您还将获得以最前沿的方式做事的温暖感觉......


为了解决 Jeff 的非常好的和相关的观点,我认为最好对泛型施加尽可能少的约束来执行任务。由于您完全控制了泛型内的代码,因此您知道您是否正在使用任何需要基本 IComparable 类型的方法。因此,考虑到他的评论,我个人会遵循以下规则:

  • 如果您不希望泛型使用实现 IComparable 的任何类型(即遗留 1.0 代码)并且您没有从泛型内部调用任何依赖于 IComparable 参数的方法,则仅使用 IComparable<> 约束。

  • 如果您使用仅实现 IComparable 的类型,则仅使用该约束

  • 如果您正在使用需要 IComparable 参数的方法,但不使用仅实现 IComparable 的类型,那么当您使用接受泛型类型的方法时,使用 Jeff 的答案中的两个约束将提高性能。

为了扩展第三条规则 - 让我们假设您正在编写的课程如下:

public class StrangeExample<T> where ... //to be decided
{
    public void SortArray(T[] input)
    {
         Array.Sort(input);
    }

    public bool AreEqual(T a, T b)
    {
        return a.CompareTo(b) == 0;
    }
}

我们需要决定对其施加什么限制。SortArray 方法调用 Array.Sort,它需要传入的数组包含实现 IComparable 的对象。因此我们必须有一个 IComparable 约束:

public class StrangeExample<T> where T : IComparable

现在该类将编译并正常工作,因为 T 数组对 Array.Sort 有效,并且接口中定义了有效的 .CompareTo 方法。但是,如果您确定不想将类与不实现 IComparable<> 接口的类型一起使用,则可以将约束扩展到:

public class StrangeExample<T> where T : IComparable, IComparable<T>

这意味着当 AreEqual 被调用时,它将使用更快、更通用的 CompareTo 方法,您将看到性能优势,但代价是无法将它与旧的 .NET 1.0 类型一起使用。

另一方面,如果您没有 AreEqual 方法,那么 IComparable<> 约束没有任何优势,因此您不妨放弃它 - 无论如何您只是在使用 IComparable 实现。

于 2009-05-27T17:03:39.080 回答
6

您可能需要这两个约束,如:

where T : IComparable, IComparable<T>

这将使您的类型与IComparable接口的更多用户兼容。的通用版本将有助于避免在值类型时装箱,IComparable并允许接口方法的更强类型的实现。支持两者可确保无论其他对象要求哪个接口,您的对象都可以遵守并因此很好地互操作。IComparable<T>T

例如,Array.SortArrayList.Sort使用IComparable,而不是IComparable<T>

于 2009-05-27T17:21:25.987 回答
2

IComparable<T>允许比较器是强类型的。

你可以有

public int CompareTo(MyType other)
{
    // logic
}

反对

public int CompareTo(object other)
{
    if (other is MyType)
        // logic
}

例如,下一个示例女巫实现了这两个接口:

public class MyType : IComparable<MyType>, IComparable
{
    public MyType(string name, int id)
    { Name = name; Id = id; }

    public string Name { get; set; }
    public int Id { get; set; }

    public int CompareTo(MyType other)
    {
        if (null == other)
            throw new ArgumentNullException("other");
        return (Id - other.Id > 0 ? 1 : 0);
    }

    public int CompareTo(object other)
    {
        if (null == other)
            throw new ArgumentNullException("other");
        if (other is MyType)
            return (Id - (other as MyType).Id > 0 ? 1 : 0);
        else
            throw new InvalidOperationException("Bad type");
    }
}


MyType t1 = new MyType("a", 1);
MyType t2 = new MyType("b", 2);
object someObj = new object();

// calls the strongly typed method: CompareTo(MyType other)
t1.CompareTo(t2);
// calls the *weakly* typed method: CompareTo(object other)
t1.CompareTo(someObj);

如果MyType仅使用 实现IComparable<MyType>,则第二个compareTo(someObj)是编译时错误。这是强类型泛型的优点之一。

另一方面,框架中有一些方法需要非泛型IComparablelike Array.Sort。在这些情况下,您应该考虑像本示例中那样实现这两个接口。

于 2009-05-27T17:02:37.773 回答
2

这是两个不同的接口。在 .NET 2.0 之前没有泛型,所以只有IComparable. .NET 2.0 出现了泛型,并且可以将IComparable<T>. 他们做的完全一样。基本上 IComparable 已经过时了,尽管大多数图书馆都承认这两者。

要使您的代码真正兼容,请同时实现两者,但要一个调用另一个,这样您就不必编写相同的代码两次。

于 2009-05-27T17:06:34.863 回答
1

我将使用第二个约束,因为这将允许您引用接口的强类型成员。如果您选择第一个选项,那么您将不得不强制转换以使用接口类型。

于 2009-05-27T17:05:44.960 回答