IComparable的接口约束

时间:2009-05-27 16:52:46

标签: c# .net generics interface icomparable

当我想约束类型T可比较时,我应该使用:

where T : IComparable

where T : IComparable<T>

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

5 个答案:

答案 0 :(得分:5)

IComparable和IComparable&lt;&gt;之间的主要区别是第一个是pre-generics所以允许你用任何对象调用compare方法,而第二个强制它共享相同的类型:

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

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


为了解决Jeff的非常好和相关的观点,我认为将一般约束放在执行任务所需的通用上是一种好习惯。由于您完全控制通用内部的代码,因此您知道是否正在使用任何需要基本IComparable类型的方法。因此,考虑到他的评论,我个人会遵循这些规则:

  • 如果您不希望泛型使用实现IComparable(即遗留1.0代码)的任何类型,并且您没有调用依赖于IComparable的泛型内部的任何方法参数然后使用IComparable&lt;&gt;仅限制。

  • 如果 使用仅实现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&lt;&gt;的类。接口,您可以将约束扩展到:

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

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

另一方面,如果你没有AreEqual方法,那么IComparable&lt;&gt;就没有任何优势。约束所以你也可以放弃它 - 你只是使用IComparable实现。

答案 1 :(得分:5)

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

where T : IComparable, IComparable<T>

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

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

答案 2 :(得分: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)是编译时错误。这是强类型泛型的一个优点。

另一方面,框架中的方法需要非通用IComparable,如Array.Sort。在这些情况下,您应该考虑实现这个示例中的两个接口。

答案 3 :(得分:2)

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

为了使你的代码真正兼容,实现两者,但是另一个调用,所以你不必两次编写相同的代码。

答案 4 :(得分:1)

我会使用第二个约束,因为这将允许您引用接口的强类型成员。如果你使用第一个选项,则必须使用界面类型进行强制转换。