C# IComparable和IComparer及泛型实现
CompareTo提供了类型的比较方法,以排序型集合对象的成员。因此,通常不从开发人员代码直接调用该方法。List<T>.Sort()和Add等方法会自动调用它.此方法只是一个定义,必须由特定的类或值类型实现才能生效。"小于","等于"和"大于"这几种比较的含义取决于具体的实现。根据定义,任何对象与null相比较都要打,两个空引用的比较结果为彼此相等。1:比较和排序的概念
比较:两个实体类之间按>,=,<进行比较。
排序:在集合类中,对集合类中的实体进行排序。排序基于的算法基于实体类提供的比较函数。
基本型别都提供了默认的比较算法,如string提供了按字母进行比较,int提供了按整数大小进行比较。
2:IComparable和IComparer
当我们创建了自己的实体类,如Student,默认想要对其按照年龄进行排序,则需要为实体类实现IComparable接口。
class Student:IComparable
{
public string Name { get; set; }
public int Age { get; set; }
#region IComparable Members
public int CompareTo(object obj)
{
Student student = obj as Student;
if (Age > student.Age)
{
return 1;
}
else if (Age == student.Age)
{
return 0;
}
else
{
return -1;
}
//return Age.CompareTo(student.Age);
}
#endregion
}
PS:注意上面代码中CompareTo方法有一条注释的代码,其实本函数完全可以使用该注释代码代替,因为利用了整形的默认比较方法。此处未使用本注释代码,是为了更好的说明比较器的工作原理。接下来写一个测试用例:public Form1()
{
InitializeComponent();
studentList = new ArrayList();
studentList.Add(new Student() { Age = 1, Name = "a1" });
studentList.Add(new Student() { Age = 5, Name = "g1" });
studentList.Add(new Student() { Age = 4, Name = "b1" });
studentList.Add(new Student() { Age = 2, Name = "f1" });
}
ArrayList studentList;
private void button1_Click(object sender, EventArgs e)
{
studentList.Sort();
foreach (Student item in studentList)
{
this.textBox1.Text += item.Name + "----" +item.Age.ToString() + "\r\n" ;
}
}运行结果:
a1----1
f1----2
b1----4
g1----5OK,疑问来了。如果不想使用年龄作为比较器了,那怎么办。这个时候IComparer的作用就来了,可使用IComparer来实现一个自定义的比较器。如下:
class SortName: IComparer
{
#region IComparer Members
public int Compare(object x, object y)
{
Student s1 = x as Student;
Student s2 = y as Student;
return s1.Name.CompareTo(s2.Name);
}
#endregion
}这个时候,我们在排序的使用为Sort方法提供此比较器:
studentList.Sort(new SortName());
运行的结果是:
a1----1
b1----4
f1----2
g1----53:IComparable和IComparer的泛型实现IComparable<T>和IComparer<T>
如果我们稍有经验,我们就会发现上面的代码我们使用了一个已经不建议使用的集合类ArrayList。当泛型出来后,所有非泛型集合类已经建议不尽量使用了。至于原因,从上面的代码中我们也可以看出一点端倪。
注意查看这个Compare函数,如:
public int Compare(object x, object y)
{
Student s1 = x as Student;
Student s2 = y as Student;
return s1.Name.CompareTo(s2.Name);
}我们发现这个函数进行了装箱和拆箱。而这是会影响性能的。如果我们的集合中有成千上万个复杂的实体对象,则在排序的时候所耗费掉的性能就是客观的。而泛型的出现,就可以避免掉拆箱和装箱。
故上文代码中的ArrayList,应该换成List<T>,对应的,我们就该实现IComparable<T>和IComparer<T>。最终的代码应该像:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
studentList = new List<Student>();
studentList.Add(new Student() { Age = 1, Name = "a1" });
studentList.Add(new Student() { Age = 5, Name = "g1" });
studentList.Add(new Student() { Age = 4, Name = "b1" });
studentList.Add(new Student() { Age = 2, Name = "f1" });
}
List<Student> studentList;
private void button1_Click(object sender, EventArgs e)
{
studentList.Sort(new SortName());
foreach (Student item in studentList)
{
this.textBox1.Text += item.Name + "----" +item.Age.ToString() + "\r\n" ;
}
}
}
class Student:IComparable<Student>
{
public string Name { get; set; }
public int Age { get; set; }
#region IComparable<Student> Members
public int CompareTo(Student other)
{
return Age.CompareTo(other.Age);
}
#endregion
}
class SortName: IComparer<Student>
{
#region IComparer<Student> Members
public int Compare(Student x, Student y)
{
return x.Name.CompareTo(y.Name);
}
#endregion
}练习:1.You are creating a class named Age. You need to ensure that the Age class is written such that collections of
Age objects can be sorted. Which code segment should you use?
A. public class Age { public int Value;
public object CompareTo(object obj)
{ if (obj is Age)
{ Age _age = (Age) obj;
return Value.CompareTo(obj); }
throw new ArgumentException("object not an Age"); } }
B. public class Age {
public int Value;
public object CompareTo(int iValue) {
try {
return Value.CompareTo(iValue); }
catch {
throw new ArgumentException ("object not an Age"); } } }
C. public class Age : IComparable {
public int Value;
public int CompareTo(object obj)
{ if (obj is Age) {
Age _age = (Age) obj;return Value.CompareTo(_age.Value); }throw new ArgumentException("object not an Age"); } }
D. public class Age : IComparable {
public int Value;
public int CompareTo(object obj) {
try {
return Value.CompareTo(((Age) obj).Value); }
catch {
return -1; } } }
Answer: C2.You are creating a class to compare a specially-formatted string. The default collation comparisons do not apply.
You need to implement the IComparable<string> interface. Which code segment should you use?
A. public class Person : IComparable<string>{ public int CompareTo(string other){ ... }}
B. public class Person : IComparable<string>{ public int CompareTo(object other){ ... }}
C. public class Person : IComparable<string>{ public bool CompareTo(string other){ ... }}
D. public class Person : IComparable<string>{ public bool CompareTo(object other){ ... }}
Answer: A