Effective C# Item26:使用IComarable和IComparer接口实现排序关系

来源:互联网 发布:js实现展开收起 编辑:程序博客网 时间:2024/05/02 06:11

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

    我们来看下面的代码。

    public struct Employee : IComparable    {        private string m_strName;        public string Name        {            get { return m_strName; }            set { m_strName = value; }        }        private int m_nAge;        public int Age        {            get { return m_nAge; }            set { m_nAge = value; }        }        public Employee(string name, int age)        {            m_strName = name;            m_nAge = age;        }        public int CompareTo(object obj)        {            if (!(obj is Employee))            {                throw new ArgumentException("Type Error!");            }            Employee emp = (Employee)obj;            return this.Name.CompareTo(emp.Name);        }    }


    上面的代码定义了一个结构体,实现了IComparable接口,接口中方法的声明方式是int CompareTo(object obj),该方法接收一个Object对象,和当前对象进行比较,如果当前对象大于比较对象,则返回结果大于0;如果当前对象小于比较对象则返回结果为-1;如果当前对象和比较对象相同,则返回0。

    下面是测试代码。

    private static void Test()    {        Employee emp1 = new Employee("Wing", 24);        Employee emp2 = new Employee("UnKnown", 25);        int nResult = emp1.CompareTo(emp2);        if (nResult > 0)        {            Console.WriteLine("emp1 is larger than emp2.");        }        else if (nResult < 0)        {            Console.WriteLine("emp1 is smaller than emp2.");        }    }


    上述代码执行后,会在命令行中输出如下内容:emp1 is larger than emp2.

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

    public struct Employee : IComparable    {        private string m_strName;        public string Name        {            get { return m_strName; }            set { m_strName = value; }        }        private int m_nAge;        public int Age        {            get { return m_nAge; }            set { m_nAge = value; }        }        public Employee(string name, int age)        {            m_strName = name;            m_nAge = age;        }        int IComparable.CompareTo(object obj)        {            if (!(obj is Employee))            {                throw new ArgumentException("Type Error!");            }            Employee emp = (Employee)obj;            return CompareTo(emp);        }        public int CompareTo(Employee emp)        {                        return this.Name.CompareTo(emp.Name);        }    }


    上述代码中,我们显示实现了IComparable接口,但是将访问限制符改为默认设置,即internal,同时添加了一个重载类型的CompareTo()方法, 接收一个Employee类型对象作为参数。这样,在执行Test()方法时,就会调用重载后的方法,绕过了装箱和拆箱。

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

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

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

    来看下面的代码。

    public class AgeComparer : IComparer<Employee>    {        public int Compare(Employee x, Employee y)        {            return x.Age.CompareTo(y.Age);        }    }    //Test Method    private static void Test()    {        List<Employee> listEmp = new List<Employee>();        listEmp.Add(new Employee("Wing", 24));        listEmp.Add(new Employee("UnKnown", 25));        listEmp.Sort();        Console.WriteLine("ouput info with default sort:");        foreach (Employee emp in listEmp)        {            Console.WriteLine(emp.Name);        }        Console.WriteLine("output info with specific sort:");        AgeComparer comparer = new AgeComparer();        listEmp.Sort(comparer);        foreach (Employee emp in listEmp)        {            Console.WriteLine(emp.Name);        }    }


    上面的代码中,首先定义了一个实现了IComparer接口的类型,该类型的Compare()方法中,以Employee的Age作为比较的依据。然后定义了一个测试方法,定义了一个元素类型是Employee类型的List,然后以两种方式对List进行排序,并输出排序后的结果。

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

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

0 0
原创粉丝点击