【问题标题】:How to Implement IComparable interface?如何实现 IComparable 接口?
【发布时间】:2011-05-10 10:27:54
【问题描述】:

我正在用一个类的实例填充一个数组:

BankAccount[] a;
. . .

a = new BankAccount[]
{
    new BankAccount("George Smith", 500m),
    new BankAccount("Sid Zimmerman", 300m)
};

填充此数组后,我想按余额对其进行排序。为此,我希望能够使用IComparable 检查每个元素是否可排序。
我需要使用接口来做到这一点。到目前为止,我有以下代码:

public interface IComparable
{
    decimal CompareTo(BankAccount obj);
}

但我不确定这是否是正确的解决方案。有什么建议吗?

【问题讨论】:

    标签: c# interface icomparable


    【解决方案1】:

    已经有IComparable<T>,但最好同时支持IComparable<T>IComparable。使用内置的Comparer<T>.Default 通常是一个更简单的选择。例如Array.Sort 会接受这样的比较器。

    【讨论】:

      【解决方案2】:

      您不应该自己定义IComparable。它已经定义好了。相反,您需要在您的 BankAccount 类上实现 IComparable

      在您定义class BankAccount 的地方,确保它实现了IComparable 接口。然后写BankAccount.CompareTo比较两个对象的余额。

      public class BankAccount : IComparable<BankAccount>
      {
          [...]
      
          public int CompareTo(BankAccount that)
          {
              if (this.Balance <  that.Balance) return -1;
              if (this.Balance == that.Balance) return 0;
              return 1;
          }
      }
      

      编辑以展示来自 cmets 的 Jeffrey L Whitledge 的解决方案:

      public class BankAccount : IComparable<BankAccount>
      {
          [...]
      
          public int CompareTo(BankAccount that)
          {
              return this.Balance.CompareTo(that.Balance);
          }
      }
      

      【讨论】:

      • 我喜欢return this.Balance.CompareTo(that.Balance);
      • @我是个女孩——我不确定你的意思。也许您不清楚我替换了代码的哪一部分。我会把所有的都放在评论里,然后:public class BankAccount : IComparable&lt;BankAccount&gt; { [...] int CompareTo(BankAccount that) { return this.Balance.CompareTo(that.Balance); } } 是不是更清楚了?
      • 另一种方式是return Balance - that.Balance;
      • 第一个版本应该是this.Balance &lt; that.Balance,按余额升序排序。 -1 = 这小于那个,0 = 这等于那个,1 = 这大于那个
      • 如果天平接近其类型的限制,return Balance - that.Balance 不是一个好主意,因为溢出可能会给您错误的结果。例如,如果 Balance 是 shortthis.Balance 是 32700,that.Balance 是 -100,则减法的结果将是 -32736,而 CompareTo 的结果显然应该是一个正数。同样,如果 Balance 是ushort,则减法的结果永远不会是负数,这显然也是错误的。
      【解决方案3】:

      IComparable 已经存在于 .NET 中,具有此 CompareTo 定义

      int CompareTo(Object obj)
      

      你不应该创建接口——你应该实现它。

      public class BankAccount : IComparable {
      
          int CompareTo(Object obj) {
                 // return Less than zero if this object 
                 // is less than the object specified by the CompareTo method.
      
                 // return Zero if this object is equal to the object 
                 // specified by the CompareTo method.
      
                 // return Greater than zero if this object is greater than 
                 // the object specified by the CompareTo method.
          }
      }
      

      【讨论】:

      • 对不起,你能举个例子说明我将如何实现它
      • 如果 obj 是 null 或不是 BankAccount 的其他类型怎么办?编辑:根据此处的 MSDN:msdn.microsoft.com/en-us/library/…:抛出 ArgumentException
      【解决方案4】:

      另一种方法是使用 LINQ 并完全跳过实现 IComparable:

      BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();
      

      【讨论】:

        【解决方案5】:

        您要破坏性地 对数组进行排序吗?也就是说,您是否要实际更改数组中项目的顺序?或者您只是想要一个按特定顺序排列的项目列表,而不破坏原始顺序?

        我建议后者几乎总是更好。考虑使用 LINQ 进行非破坏性排序。 (并考虑使用比“a”更有意义的变量名。)

        BankAccount[] bankAccounts = { whatever };
        var sortedByBalance = from bankAccount in bankAccounts 
                              orderby bankAccount.Balance 
                              select bankAccount;
        Display(sortedByBalance);
        

        【讨论】:

        • 我想破坏它实现 icompare
        • @Lippert:虽然这是一个非常有效的回应,但从讨论中看来,OP 几乎不明白实现接口意味着什么。她可能还没有准备好回答你提出的问题。
        • 嗨,Eric,在实现 IComparable&lt;T&gt; 和子类化 Comparer&lt;T&gt;(假设 T 是引用类型)时,处理 null 的最佳做法是什么?它取决于用户案例还是抛出异常通常更好,因为真正的比较逻辑通常被转发到T上的某些属性。
        • @stt106:这听起来像是个问题;考虑将其作为问题发布。简短回答:我将对所有可能的值(包括 null)实施总排序。传统上,null 小于所有其他可能性。也就是说,如果您认为提供 null 总是错误,那么抛出异常可能是明智的。
        【解决方案6】:

        如果您只需要对这些BankAccounts 进行排序,请使用LINQ,如下所示

        BankAccount[] a = new BankAccount[]
        {
            new BankAccount("George Smith", 500m),
            new BankAccount("Sid Zimmerman", 300m)
        };
        
        a = a.OrderBy(bank => bank.Balance).ToArray();
        

        【讨论】:

          【解决方案7】:

          如果需要比较多个字段,可以使用新的元组语法从编译器获得一些帮助:

          public int CompareTo(BankAccount other) =>
            (Name, Balance).CompareTo(
              (other.Name, other.Balance));
          

          这可以扩展到任意数量的属性,并且会按照您的预期逐一比较它们,从而使您不必实现许多 if 语句。

          请注意,您也可以使用此元组语法来实现其他成员,例如GetHashCode。只需构造元组并在其上调用GetHashCode

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多