zoukankan      html  css  js  c++  java
  • 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#真的是一门优雅优秀的语言,而且微软开放了部分源代码,值得长期投资。

  • 相关阅读:
    [译文] 实体与值对象到底是不是一回事?
    实现 WebApi 自托管服务宿主于 WinForms 及其交互
    [译文] C# 8 已成旧闻, 向前, 抵达 C# 9!
    [译文] 为什么你在 C# 里总是应该使用 "var" 关键字
    通过设置iis在局域网中访问网页
    windows 10 安装使用kafka
    ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly实现瞬时故障处理
    ASP.NET Core 2.1 中的 HttpClientFactory (Part 3) 使用Handler实现传出请求中间件
    ASP.NET Core 2.1 中的 HttpClientFactory (Part 2) 定义命名化和类型化的客户端
    Asp.net Core 2.0 OpenId Connect Handler缺失Claims?
  • 原文地址:https://www.cnblogs.com/leemano/p/4926637.html
Copyright © 2011-2022 走看看