【问题标题】:Interface constraint for IComparableIComparable 的接口约束
【发布时间】:2010-10-29 08:19:13
【问题描述】:

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

where T : IComparable

where T : IComparable<T>

如果#2 有意义,我无法理解。谁能解释一下有什么区别?

【问题讨论】:

    标签: c# .net generics interface icomparable


    【解决方案1】:

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

    where T : IComparable, IComparable<T>
    

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

    例如,Array.SortArrayList.Sort 使用 IComparable,而不是 IComparable&lt;T&gt;

    【讨论】:

      【解决方案2】:

      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 实现。

      【讨论】:

      • 这并不完全正确,因为 BCL 的某些元素仅使用 IComparable 而不是 IComparable,例如 Array.Sort 和 ArrayList.Sort。确实,约束应该强制两个接口。
      • 谢谢马丁。你能解释一下你写的最后一条规则吗?你的意思是如果我有两个接口并使用通用比较,性能会更好吗?
      • 是的,因为使用 IComparable 约束的主要优势是性能优势,如果您必须支持 IComparable 类型,因为您正在使用需要它的方法(例如 Array.Sort),但您 可以 支持 IComparable 类型,因为您没有使用任何未实现它的旧类型,那么您最好同时要求两者并将对象用作尽可能快地打字。
      • 如果一个人想使用 Array.Sort 而一个人有一个IComparable&lt;T&gt;,有什么理由不能使用 `Array.Sort(theArray, Comparer.Default)?
      【解决方案3】:

      这是两个不同的界面。在 .NET 2.0 之前没有泛型,所以只有 IComparable。 .NET 2.0 出现了泛型,并且可以生成 IComparable&lt;T&gt;。他们做的完全一样。基本上 IComparable 已经过时了,尽管大多数库都承认这两者。

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

      【讨论】:

        【解决方案4】:

        IComparable&lt;T&gt; 允许比较器是强类型

        你可以拥有

        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&lt;MyType&gt; 实现,则第二个compareTo(someObj) 是编译时错误。这是强类型泛型的优势之一。

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

        【讨论】:

        • bruno:在您的代码中,我宁愿将 CompareTo(object) 中的比较作业委托给 CompareTo(object) 的第二个分支中的 CompareTo(MyType),而不是复制代码。
        【解决方案5】:

        我将使用第二个约束,因为它允许您引用接口的强类型成员。如果您选择第一个选项,则必须强制转换以使用接口类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-11
          • 1970-01-01
          • 1970-01-01
          • 2011-05-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多