zoukankan      html  css  js  c++  java
  • Effective C# Item26:使用IComarable和IComparer接口实现排序关系

        .NET框架定义了两个接口来描述类型的排序关系:IComparable和IComparer,其中IComparable接口定义了类型的自然排序方式,IComparer则为类型提供了另外的排序方式。

        我们来看下面的代码。

    代码
    1 public struct Employee : IComparable
    2 {
    3 private string m_strName;
    4 public string Name
    5 {
    6 get { return m_strName; }
    7 set { m_strName = value; }
    8 }
    9
    10 private int m_nAge;
    11 public int Age
    12 {
    13 get { return m_nAge; }
    14 set { m_nAge = value; }
    15 }
    16
    17 public Employee(string name, int age)
    18 {
    19 m_strName = name;
    20 m_nAge = age;
    21 }
    22
    23 public int CompareTo(object obj)
    24 {
    25 if (!(obj is Employee))
    26 {
    27 throw new ArgumentException("Type Error!");
    28 }
    29 Employee emp = (Employee)obj;
    30 return this.Name.CompareTo(emp.Name);
    31 }
    32 }
        上面的代码定义了一个结构体,实现了IComparable接口,接口中方法的声明方式是int CompareTo(object obj),该方法接收一个Object对象,和当前对象进行比较,如果当前对象大于比较对象,则返回结果大于0;如果当前对象小于比较对象则返回结果为-1;如果当前对象和比较对象相同,则返回0。

        下面是测试代码。

    代码
    1 private static void Test()
    2 {
    3 Employee emp1 = new Employee("Wing", 24);
    4 Employee emp2 = new Employee("UnKnown", 25);
    5 int nResult = emp1.CompareTo(emp2);
    6 if (nResult > 0)
    7 {
    8 Console.WriteLine("emp1 is larger than emp2.");
    9 }
    10 else if (nResult < 0)
    11 {
    12 Console.WriteLine("emp1 is smaller than emp2.");
    13 }
    14 }
        上述代码执行后,会在命令行中输出如下内容:emp1 is larger than emp2.

        我们再来看上面的代码,其中Employee类型是一个结构体,属于值类型,但是IComparable接口的方法接收参数的类型是Object,这样我们在CompareTo()方法中必须进行装箱和拆箱的操作,才能完成比较操作。这样做对性能的影响比较大,特别是需要比较的数据非常多的时候,例如一个大数据量的集合。为了解决我们需要对上述Employee类型的代码进行修改,修改后的代码如下。

    代码
    1 public struct Employee : IComparable
    2 {
    3 private string m_strName;
    4 public string Name
    5 {
    6 get { return m_strName; }
    7 set { m_strName = value; }
    8 }
    9
    10 private int m_nAge;
    11 public int Age
    12 {
    13 get { return m_nAge; }
    14 set { m_nAge = value; }
    15 }
    16
    17 public Employee(string name, int age)
    18 {
    19 m_strName = name;
    20 m_nAge = age;
    21 }
    22
    23 int IComparable.CompareTo(object obj)
    24 {
    25 if (!(obj is Employee))
    26 {
    27 throw new ArgumentException("Type Error!");
    28 }
    29 Employee emp = (Employee)obj;
    30 return CompareTo(emp);
    31 }
    32
    33 public int CompareTo(Employee emp)
    34 {
    35 return this.Name.CompareTo(emp.Name);
    36 }
    37 }
        上述代码中,我们显示实现了IComparable接口,但是将访问限制符改为默认设置,即internal,同时添加了一个重载类型的CompareTo()方法, 接收一个Employee类型对象作为参数。这样,在执行Test()方法时,就会调用重载后的方法,绕过了装箱和拆箱。

        注意:在实现了IComparable接口后,不需要重写Equals()方法,因为大部分情况下,Equals()方法是针对对象的引用进行比较,而IComparable接口是针对对象中的内容进行比较;因此可以出现以下的情况:两个对象调用CompareTo()方法后返回为0,但是调用Equals()方法后返回false。

        另外,如果我们实现了IComparable接口,一般情况下需要对操作符进行重载,这些操作符包括:<、>、<=、>=和!=。

        如果我们需要自己定制比较规则,那么我们可以通过实现IComparer接口来实现这个目标。

        来看下面的代码。

    代码
    1 public class AgeComparer : IComparer<Employee>
    2 {
    3 public int Compare(Employee x, Employee y)
    4 {
    5 return x.Age.CompareTo(y.Age);
    6 }
    7 }
    8
    9
    10 //Test Method
    11 private static void Test()
    12 {
    13 List<Employee> listEmp = new List<Employee>();
    14 listEmp.Add(new Employee("Wing", 24));
    15 listEmp.Add(new Employee("UnKnown", 25));
    16 listEmp.Sort();
    17 Console.WriteLine("ouput info with default sort:");
    18 foreach (Employee emp in listEmp)
    19 {
    20 Console.WriteLine(emp.Name);
    21 }
    22
    23 Console.WriteLine("output info with specific sort:");
    24 AgeComparer comparer = new AgeComparer();
    25 listEmp.Sort(comparer);
    26 foreach (Employee emp in listEmp)
    27 {
    28 Console.WriteLine(emp.Name);
    29 }
    30 }
        上面的代码中,首先定义了一个实现了IComparer接口的类型,该类型的Compare()方法中,以Employee的Age作为比较的依据。然后定义了一个测试方法,定义了一个元素类型是Employee类型的List,然后以两种方式对List进行排序,并输出排序后的结果。

        上面Test()方法的执行结果如下所示。

        综上,IComparable接口和IComparer接口为类型实现排序关系提供了两种标准的机制,IComparable接口应该用于为类型实现最自然的排序关系,而ICpmparer接口则用于定制排序的方式。

  • 相关阅读:
    Java闭包和回调
    Java通过字节分割字符串
    编译型语言和解释型语言的简单介绍
    对JavaScript事件处理程序/事件监听器的设定的简单介绍
    Linux netstat命令详解
    CentOS 7下iptables配置添加修改规则端口方法(转)
    设计模式
    设计模式
    Mysql8.0主从配置
    设计模式
  • 原文地址:https://www.cnblogs.com/wing011203/p/1651961.html
Copyright © 2011-2022 走看看