C# 常用接口學習 IComparable 和 IComparer


C# 常用接口學習 IComparable IComparer

作者:烏龍哈里
時間:2015-11-01
平台:Window7 64bit,Visual Studio Community 2015

參考:

章節:

  • 接口 IConmparable 實現
  • 接口 IComparable<T> 實現
  • 接口 IComparer<T> 實現

正文:

一、接口 IConmparable 的實現

class Fruit
{
    public string Name;
    public int Price;
}

List<Fruit> fruit = new List<Fruit>();
fruit.Add(new Fruit { Name = "grape", Price = 30 });
fruit.Add(new Fruit { Name = "apple", Price = 10 });
fruit.Add(new Fruit { Name = "banana", Price = 15 });
fruit.Add(new Fruit { Name = "orage", Price = 12 });

我們如果想要把水果類按價錢排序,用 Sort() 方法,就需要在 Fruit 類實現 IComparable 接口了。
去看了net4.0的公開源代碼(見參考),只有一個方法:
int CompareTo(Object obj);
微軟的技術人員在注釋里說明了,等於返回0值,大於返回大於0的值,小於返回小於0的值。下面我們按說明來實現這個接口:

class Fruit : IComparable
{
    public string Name;
    public int Price;
    public int CompareTo(object obj)
    {
        //實現接口方法一:
        if (obj == null) return 1;
        Fruit otherFruit = obj as Fruit;
        if (Price > otherFruit.Price) { return 1; }
        else
        {
            if (Price == otherFruit.Price) { return 0; }
            else { return -1; }
        }
    }
}

測試 Sort() 輸出:

static void Main(string[] args)
{
    List<Fruit> fruit = new List<Fruit>();
    fruit.Add(new Fruit { Name = "grape", Price = 30 });
    fruit.Add(new Fruit { Name = "apple", Price = 10 });
    fruit.Add(new Fruit { Name = "banana", Price = 15 });
    fruit.Add(new Fruit { Name = "orage", Price = 12 });

    Console.Write("未排序:");
    foreach (var f in fruit) Console.Write($"{f.Name} ");
    Console.WriteLine();

    fruit.Sort();

    Console.Write("排序后:");
    foreach (var f in fruit) Console.Write($"{f.Name} ");
    Console.WriteLine();
    Console.ReadKey();
}

//輸出結果:
//未排序:grape apple banana orage
//排序后:apple orage banana grape

要是要按照 Name 來排序,只要把 Price 換成 Name就成了。
我們發現,所有的簡單值類型(比如:int ,string 等等)都繼承了這個 IComparable 接口的,我們可以借用值類型的 CompareTo() 方法來實現:

class Fruit : IComparable
{
    public string Name;
    public int Price;
    public int CompareTo(object obj)
    {
        //實現接口方法二:
        Fruit otherFruit = obj as Fruit;
        return Price.CompareTo(otherFruit.Price);
    }
}

對於這個 IComparable 接口,因為基本簡單的值類型都有 CompareTo() 方法,而且有了 Linq 后,我只要能用 IEnumerable<T> 的集合類型,用 lambda 表達式很容易就能進行排序 Sort() 操作,而且升序降序排序更加方便。上面的例子中,就算 Fruit 類沒有實現 IComparable 接口,我用 List<T>,然后用 lambda 表達式生成一個新的集合就成了,如下:

    class Program
    {
        static void Main(string[] args)
        {
            List<Fruit> fruit = new List<Fruit>();
            fruit.Add(new Fruit { Name = "grape", Price = 30 });
            fruit.Add(new Fruit { Name = "apple", Price = 10 });
            fruit.Add(new Fruit { Name = "banana", Price = 15 });
            fruit.Add(new Fruit { Name = "orage", Price = 12 });

            Console.Write("排序前:");
            foreach (var f in fruit) Console.Write($"{f.Name} ");
            Console.WriteLine();

            var fruitSort = fruit.OrderBy(x => x.Price);

            Console.Write("排序后:");
            foreach (var f in fruitSort) Console.Write($"{f.Name} ");
            Console.WriteLine();

            Console.ReadKey();
        
        }
    }
    class Fruit
    {
        public string Name;
        public int Price;
    }

真的弄不清楚到底這個接口有了泛型和 Linq 后還有什么用。本着認真學習的態度,我們繼續學習它的泛型接口。

二、接口 IConmparable<T> 的實現

我們看到在實現 IComparable 中會發生裝箱行為:
Fruit otherFruit=obj as Fruit;
為了效率就引進了泛型,實現如下:

    class Fruit : IComparable<Fruit>
    {
        public string Name;
        public int Price;
        int IComparable<Fruit>.CompareTo(Fruit other)
        {
            return Price.CompareTo(other.Price);
        }
    }

調用和普通的一樣調用。現在還有個一問題,就是我們在章節一里面所說的,這個 Fruit 類是按字段 Price 來排序的,要是我們想按 Name 來排序,修改 int CompareTo() 方法很不靈活和方便,這時我們就需要另外一個類似的接口 IComparer 來提供便利了。

三、接口 IConmparer<T> 的實現

這個 IComparer 我理解為比較器接口,章節一的 Fruit 類接口 IComparable 不改,要想按 Name 來排序,需創立一個新類,在其上實現 IComparer<T> 接口:

    class SortByName : IComparer<Fruit>
    {
        int IComparer<Fruit>.Compare(Fruit x, Fruit y)
        {
            return x.Name.CompareTo(y.Name);
        }
    }
    class Fruit : IComparable
    {
        public string Name;
        public int Price;
        public int CompareTo(object obj)
        {
            //實現接口方法二:
            Fruit otherFruit = obj as Fruit;
            return Price.CompareTo(otherFruit.Price);
        }
    }

調用: fruit.Sort(new SortByName());
這個接口給我們帶來重大的靈活性和方便性。不過我還是認為在泛型和lambda范式后,這個玩意好像真的沒多大用處。

本文到此結束。

題外話:C#真的是一門優雅優秀的語言,而且微軟開放了部分源代碼,值得長期投資。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM