.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接口则用于定制排序的方式。
|
请发表评论