IComparable和IComparer接口是.NET Framework中比较对象的标准方式。这两个接口之间的区别如下:
IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象。
IComparer在一个单独的类中实现,可以比较任意两个对象。
一般使用IComparable给出类的默认比较带啊,使用其他类给出非默认的比较代码。
IComparable提供了一个方法CompareTo(),这个方法接受一个对象。例如,在实现该方法时,使其可以接受一个Person对象,以便确定这个人比当前的人更年老还是更年轻。实际上,这个方法返回一个int,所以也可以确定第二个人与当前的人的年龄差:
if(person1.Compareto(person2) == 0)
{
WriteLine("Same age");
}
else if(person1.CompareTo(person2) > 0)
{
WriteLine("Person 1 is older");
}
else
{
WriteLine("Person 2 is older");
}
IComparer也提供一个方法Compare()。这个方法接受两个对象,返回一个整形结果,这与CompareTo()相同。对于支持IComparer的对象,可使用下面的代码:
if(personComparer.Compare(person1, person2) == 0)
{
WriteLine("Same age");
}
else if(personComparer.Compare(person1, person2) > 0)
{
WriteLine("Person 1 is older");
}
else
{
WriteLine("Person 2 is older");
}
这两种情况下,提供给方法的参数是System.Object类型。这意味者可以比较一个对象与其他任意类型的另一个对象。所以,在返回结果之前,通常需要进行某种类型比较,如果使用了错误类型,还会抛出异常。
.NET Framework在类Comparer上提供了IComparer接口的默认实现代码,类Compare位于System.Collections的名称空间中,可以对简单类型以及支持IComparable接口的任意类型进行特定文化的比较。例如,可通过下面的代码使用它:
string firstString = "First String";
string secondString = "Second String";
WriteLine($"Comparing '{firstString}' and '{secondString}',"+$"result:{Comparer.Default.Compare(firstString, secondString)}");
int firstNumber = 35;
int secondNumber = 23;
WriteLine($"Comparing '{firstNumber}' and '{secondNumber}',"+$"result:{Comparer.Default.Compare(firstNumber, secondNumber)}");
这里使用Compare.Default静态成员获取Comparer类的一个实例,接着使用Compare()方法比较前两个字符串,之后比较两个整数,结果如下:
Comparing 'First String' and 'Second String', result: -1
Comparing '35' and '23',result: 1
在字母表中,F在S的前面,所以F“小于”S,第一个比较的结果就是-1。同样,35大于23,所以结果是1。注意这里的结果并未给出相差的幅度。
在使用Comparer时,必须使用可以比较的类型。例如,试图比较firstString和firstNumber就会生成一个异常。
下面列出了有关这个类的一些注意事项:
1)检查传递给Comparer.Compare()的对象,看看它们是否支持IComparable。如果支持,就使用该实现代码。
2)允许使用null值,它被解释为“小于”其他的任意对象。
3)字符串根据当前文化来处理。要根据不同的文化(或语言)处理字符串,Comparer类必须使用其构造函数进行实例化,以便传送与指定所使用的文化的System.Globalization.CultureInfo对象。
4)字符串在处理时要区分大小写。如果要以不区分大小写的方式来处理它们,就需要使用CaseInsensitiveComparer类,该类以相同的方式工作。
许多集合类可以用对象的默认方式进行排序,或者用定制方法来排序。ArrayList就是一个实例,它包含方法Sort(),这个方法使用时可以不带参数,此时使用默认的比较方式,也可以给他传递IComparer接口,以比较对象对。
给ArrayList填充了简单类型时,例如整数或字符串,就会进行默认的比较。对于自己的类,必须在类定义中实现IComparable,或创建一个支持IComparer的类,来进行比较。
注意,System.Collections名称空间中的一些类(包括CollectionBase)都没有提供排序方法。如果要对派生于这个类的集合排序,就必须多做一些工作,自己给内部的List集合排序。
添加一个新类Person:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ch11Ex05
{
class Person : IComparable
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
public int CompareTo(object obj)
{
if (obj is Person)
{
Person otherPerson = obj as Person;
return this.Age - otherPerson.Age;
}
else
{
throw new ArgumentException(
"Object to compare to is not a Person object.");
}
}
}
}
添加一个新类PersonCompareName:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ch11Ex05
{
public class PersonComparerName : IComparer
{
public static IComparer Default = new PersonComparerName();
public int Compare(object x, object y)
{
if (x is Person && y is Person)
{
return Comparer.Default.Compare(
((Person)x).Name, ((Person)y).Name);
}
else
{
throw new ArgumentException(
"One or both objects to compare are not Person objects.");
}
}
}
}
修改Program.cs中的代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Ch11Ex05
{
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
list.Add(new Person("Rual", 30));
list.Add(new Person("Donna", 25));
list.Add(new Person("Mary", 27));
list.Add(new Person("Ben", 44));
WriteLine("Unsorted people:");
for (int i = 0; i < list.Count; i++)
{
WriteLine($"{(list[i] as Person).Name } ({(list[i] as Person).Age })");
}
WriteLine();
WriteLine(
"People sorted with default comparer (by age):");
list.Sort();
for (int i = 0; i < list.Count; i++)
{
WriteLine($"{(list[i] as Person).Name } ({(list[i] as Person).Age })");
}
WriteLine();
WriteLine(
"People sorted with nondefault comparer (by name):");
list.Sort(PersonComparerName.Default);
for (int i = 0; i < list.Count; i++)
{
WriteLine($"{(list[i] as Person).Name } ({(list[i] as Person).Age })");
}
ReadKey();
}
}
}