专栏首页喵叔's 专栏利用 IComparable<T> 以及 IComparer<T> 定义顺序关系

利用 IComparable<T> 以及 IComparer<T> 定义顺序关系

我们都知道,在开发中如果想把某个类型的对象放入集合中执行排序和搜索功能,就需要定义出来对象与对象之间的关系。那么你知道怎么样定义对象关系才是正确的吗?下面就听我一一道来。

零、讲解

在 .NET 中有两个接口可以用来定义关系,即 IComparableIComparer 。前者用来规定某类型的给对象之间所具备的自然顺序,后者用来表示另一种排序机制可以有需要提供排序功能的类型来实现。 IComparable 接口只有一个方法 CompareTo ,该方法遵循如下的惯例:如果本对象小于另一个受测对象,就返回小于 0 的值,如果相等就返回 0 ,如果大于受测对象就返回大于 0 的值。这里需要注意的是在新的 .NET API 中大部分都使用了 IComparable ,而在一些老的 API 中使用的依然是不带泛型的 IComparable 接口,所以我们在实现 IComparable 的时候就必须实现 IComparable 。并且由于 IComparable 的 CompareTo 方法需要一个 object 类型的参数,因此我们需要检查传入参数的运行期类型,就是说每次进行对比前我们要判断它是否是指定的类型,如果不是就抛出异常反之继续执行后续代码。下面的代码就同时实现了 IComparable 和 IComparable。

public class User:IComparable<User>,IComparable
{
    private readonly string name;
    public User(string name)
    {
        this.name=name;
    }

    //实现 IComparable<T> 中的 CompareTo 方法
    public int CompareTo(User user) => name.CompareTo(user.name);

    //实现 IComparable 中的 CompareTo 方法
    int IComparable.CompareTo(object obj)
    {
        if(!(obj is User))
        {
            throw new ArgumentException("传入的参数不是 User 类型!");
        }
        User user=(User)obj;
        return this.CompareTo(user);
    }
}

在上述代码中我们看到了一个奇怪的代码行 int IComparable.CompareTo(object obj),明确的限定了这个方法只能通过 IComparable 来调用,这就说明了它是专门留给老版本 API 使用的。现在大部分开发人员都不怎么喜欢非泛型的 IComparable ,主要是因为它要检查传入参数的运行期类型,并且每次作比较的时候有很大的可能性会触发装箱和拆箱操作,我们都知道装箱和拆箱操作是一个很费时的事情。而且因为 IComparable.CompareTo 的对比次数为 nlog(n) 次,因此每次进行比较时基本上会执行装箱和拆箱操作,这样的话要执行三次。因此对于大数据的排序对比的耗时将是非常恐怖的。 到这里一定有读者会问:IComparable.CompareTo 缺点这么大为什么还要实现它呢?其实原因很简单,首先为了保证向后兼容,当我门需要和老版本的 API 进行交互的时候这一点是非常重要的,其次有些时候开发人员只能/必须调用 IComparable.CompareTo 。我们在实现 IComparable 的时候必须限定这个版本相关的方法只能通过 IComparable 形式的引用来调用,同时还要提供强类型的公共重载版本用来提升程序执行效率,还能防止开发人员用错 CompareTo 方法。 到这里我们的代码并没有完成,我们还需要利用 CompareTo 方法重载关系运算符:

public class User:IComparable<User>,IComparable
{
    private readonly string name;
    public User(string name)
    {
        this.name=name;
    }

    //实现 IComparable<T> 中的 CompareTo 方法
    public int CompareTo(User user) => name.CompareTo(user.name);

    //实现 IComparable 中的 CompareTo 方法
    int IComparable.CompareTo(object obj)
    {
        if(!(obj is User))
        {
            throw new ArgumentException("传入的参数不是 User 类型!");
        }
        User user=(User)obj;
        return this.CompareTo(user);
    }

    #region 重载运算符
    public static bool operator < (User user1,User user2)
    {
        return user1.CompareTo(user2) < 0;
    }
    public static bool operator > (User user1,User user2)
    {
        return user1.CompareTo(user2) > 0;
    }
    public static bool operator <= (User user1,User user2)
    {
        return user1.CompareTo(user2) <= 0;
    }
    public static bool operator >= (User user1,User user2)
    {
        return user1.CompareTo(user2) >= 0;
    }

    #endregion
}

上面的代码只是针对 name 进行了排序,那么如果我们需要按照 age 进行排序怎么办?难道我们要删掉 name 替换成 age 吗?当然不是,我们可以利用 Comparison形式的委托实现,这样我们就可以按照其他指标进行排列。具体的用法是在 User 类中增加一个静态属性,并且采用其他指标来定义对象与对象之间的顺序。

public static Comparison<User> CompareByAge=>(user1,user2)=>user1.age.CompareTo(user2.age);

Tip:大部分基础教程的书上会讲解利用嵌套类的方式实现按照另一种方式排序,这个我只建议大家了解即可,因为这是针对 .NET 1.X 的接口来说的。目前几乎没有任何公司、个人、机构在使用 .NET 1.X了,因此本文不进行讲解。

一、总结

在我们自定义类型的时候,IComparable 和 IComparer 是定义排序关系的标准,大部分排序基本上都可以通过 IComparable 来实现,但是我们需要主义的时这个时候我们必须重载运算符 <、>、<=、>= 这样才能产生出与 IComparable 相协调的结果。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 通过运行期类型检查实现泛型算法

    虽然我们可以通过指定不同的类型参数来实现泛型类的复用,但是在某些情况下通用就意味着我们无法利用具体类型的优势。针对这一点 C# 允许在发现类型参数所表示的对象具...

    喵叔
  • C# 关键字之 decimal

    decimal 关键字指示 128 位数据类型。 与浮点型相比,decimal 类型具有更高的精度和更小的范围,这使它适合于财务和货币计算。 decimal 类...

    喵叔
  • Scrapy 爬虫模板--CrawlSpider

    从这篇文章开始,我将利用三篇文章分别讲解 Scrapy 爬虫模板。 Scrapy 爬虫模板包含四个模板:

    喵叔
  • CTF实战30 CTF题目练习和讲解五

    该培训中提及的技术只适用于合法CTF比赛和有合法授权的渗透测试,请勿用于其他非法用途,如用作其他非法用途与本文作者无关

    用户1631416
  • python---rsa加密根据指数和模生成加密参数模板

    小小咸鱼YwY
  • 接口隔离原则

    1. Clients should not be forced to depend upon interfaces that they don't use.(客...

    LieBrother
  • 小议Python列表和元组中的元素地址连续性

    众所周知,在Python中字典和集合依赖元素哈希表来存储,并不存在传统意义上的所谓元素“顺序”,当然,如果需要一个有序的字典可以使用collections模块提...

    Python小屋屋主
  • 管理小型技术团队的方法

    相对于大公司来讲,创业型公司所拥有的人才可能没有大公司那样齐全,很多岗位也是缺胳膊少腿,但是在创业型的团队中总会有那么几个很符合需求的人,这几个人,就是所谓特种...

    韩伟
  • Tensorflow入门教程(十八)——特征点检测案例

    特征点检测的应用有很多种,比如人脸特征点检测,人体骨架特征点检测,人体运动特征点检测等。今天我就以人脸特征点为例,通过卷积神经网络来实现检测。

    医学处理分析专家
  • 基于STM32CUBE的USB键盘例程.docx

    前面说了USB鼠标,这次趁热打铁,说一下USB键盘。依然只说如何修改,不说背后的原理。原因你懂的,涉及的知识点太多了。

    MCU起航

扫码关注云+社区

领取腾讯云代金券