• C#排序比较


    与C#定义了相等性比较规范一样,C#也定义了排序比较规范,以确定一个对象与另一个对象的先后顺序。排序规范如下

    • IComparable接口(包括IComparable接口和IComparable<T>接口)
    • >和<运算符

    当需要实现排序算法时,使用IComparable接口。在下面的例子中,Array.Sort静态方法可以调用,是因为System.String类实现了IComparable接口。

    string[] colors={"Green", "Red", "Blue"};
    Array.Sort(colors)
    foreach(string c in colors)
        Console.Write(c+ " ");

    而<和>运算符比较特殊,因为他们一般用于比较数字类型。因为大于和小于运算符会被静态地解析,因此它们“产生”出高效的代码,适用于复杂计算的场景。

    .NET Framework还提供了插件式的排序协议--IComparer接口。IComparable接口与IComparer接口的差别类似与IEquatable和IEqualityComparer接口 (关于IEqutable接口和IEqualityComparer接口,请参考C#相等性:http://www.cnblogs.com/yang_sy/p/3582946.html)

    1. IComparable接口

    IComparable接口的定义如下

    public interface IComparable
    
        int CompareTo(Object obj);
    }
    
    public interface IComparable<in T>
    {
        int CompareTo(T other);
    }

    这两个接口定义了相同的功能。对于值类型,IComparable<T>接口效率高于ICompare接口。上面的两个接口的CompareTo方法都按照下面的方式运行:

    • 如果a排在b后面,那么a.CompareTo(b)返回1
    • 如果a和不一样,那么返回0
    • 如果a排在不前面,那么返回-1

    我们来看下面的示例代码:

    IList<Staff> staffs = new List<Staff> 
    {
        new Staff{FirstName="AAA", Title="Manager", Dept="Sale"},  
        new Staff{FirstName="BBB", Title="Accountant", Dept="Finance"},
        new Staff{FirstName="CCC", Title="Accountant", Dept="Finance"},
    };
    
    Console.WriteLine("BBB".CompareTo(staffs[0].FirstName)); // 1
    Console.WriteLine("BBB".CompareTo(staffs[1].FirstName)); // 0
    Console.WriteLine("BBB".CompareTo(staffs[2].FirstName)); // -1

    C#的大部分基本类型都实现了IComparable接口和IComparable<T>接口。很多自定义类型同样也实现了该接口,这样便于排序。

    IComarable与Equals

    假设一个类型重写了Equals方法并实现了IComparable接口。那么你肯定希望当Equals返回true时,CompareTo应当返回0。而Equals返回false时,CompareTo可以返回任何值。

    换句话说,相等性比对比性更严格;反之则不会。因此,当CompareTo说“两个对象相等”时,Equals会说“这两个对象不一定相等”。一个很好的例子来自System.String类。String.Equals方法和==运算符使用序号排序规则比较字符串--也就是通过每个字符的Unicode的值进行排序。而String.CompareTo方法,却使用不那么严格的基于文化区域(culture-dependent)进行比较。对于大多数计算机,字符ǖ和ṻ,Equals返回False,而CompareTo返回0

    你可以实现通过IComparer接口,从而完成特定的排序算法。自定义IComparer接口的实现,进一步加大了CompareTo和Equals方法之间的差异。比如不区分大小写的字符串比较器,对于A和a,将返回0. 这也从反面印证了,ComparTo方法不如Equals方法严格。

    2. <和>运算符

    一些类型,定义了<和>运算符,比如:

    bool after2010 = DateTime.Now > new DateTime(2010, 1, 1);
    Console.WriteLine(after2010);

    当实现<和>运算符之后,你需要保证<和>运算符与IComparable接口保持一致。这也是.NET Framework的标准。

    同样地,当一个类型重载了<和>运算符,那么也要求实现IComparable接口,而反之则不需要。实际上,大多数.NET类型实现了IComparable接口,并没有重载<和>运算符。这(排序比较)与相等性比较不一样:

    • 在实现相等性比较时,如果重载了Equals方法,那么一般都重载==运算符
    • 而在实现排序性比较时,如果实现了CompareTo方法,一般不要求重载<运算符和>运算符

    一般地,只有在下面的情形中,才需要重载<运算符和>运算符:

    • 一个类型本身包含大于和小于这样的概念
    • 执行先后顺序比较的方式是唯一的
    • 结果不会随文化区域(Cultures)变化而变化

    System.Stirng类型不满足最后一条,因此string不支持>操作和<操作。因此 “beck” > “Anne”,编译时会抛出错误。

    3. 实现IComparable接口

    下面的实例代码中,结构Note表示一个音乐的注释,它实现了IComparable接口,还重载了<运算符和>运算符。为了实例的完整性,我们还重写了Equals和GetHashCode方法,以及重载了==和!=运算符,通过这个例子,你可以全面的了解排序比较。

    internal struct Note : IComparable, IComparable<Note>, IEquatable<Note>
    {
    
        private int semitonesFromA;
        public int SemitonesFromA
        {
            get { return semitonesFromA; }
        }
    
        public Note(int semitonesFromA)
        {
            this.semitonesFromA = semitonesFromA;
        }
    
        // generic IComparable<T>
        public int CompareTo(Note other)
        {
            if (Equals(other))
                return 0;
            return SemitonesFromA.CompareTo(other.SemitonesFromA);
        }
    
        // non-generic IComaparable
        public int IComparable.CompareTo(object other)
        {
            if (!(other is Note))
                throw new InvalidOperationException("CompareTo: Not a note");
            return CompareTo((Note)other);
        }
    
        public static bool operator <(Note n1, Note n2)
        {
            return n1.CompareTo(n2) < 0;
        }
    
        public static bool operator >(Note n1, Note n2)
        {
            return n1.CompareTo(n2) > 0;
        }
    
        // for IEquatable
        public bool Equals(Note other)
        {
            return this.SemitonesFromA == other.SemitonesFromA;
        }
    
        // override Object.Equals
        public override bool Equals(object other)
        {
            if (!(other is Note))
                throw new InvalidOperationException("CompareTo: Not a note");
            return Equals((Note)other);
        }
    
        public override int GetHashCode()
        {
            return SemitonesFromA.GetHashCode();
        }
    
        public static bool operator ==(Note n1, Note n2)
        {
            return n1.Equals(n2);
        }
    
        public static bool operator !=(Note n1, Note n2)
        {
            return !(n1 == n2);
        }
    }
  • 相关阅读:
    eslint and stylelint config
    CSS3 animaion 和 transition 比较
    css之px、em、rem
    Three.js 中 相机的常用参数含义
    ES6中函数调用自身需要注意的问题
    MySQL数据库迁移之data目录
    ES6扩展运算符(三点运算符)...的用法
    Vue 组件通信方案
    关于ES6中Promise的应用-顺序合并Promise,并将返回结果以数组的形式输出
    Ubuntu 使用gps
  • 原文地址:https://www.cnblogs.com/yang_sy/p/3593507.html
Copyright © 2020-2023  润新知