Effective C# Item26:使用IComarable和IComparer接口實現排序關係

    .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接口則用於定製排序的方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章