IComparable<T>

.NET 里,IComparable<T>是用来作比较的最常用接口。

如果某个类型的实例需要与该类型的其它实例进行比较或者排序的话,那么该类型就可以通过实现IComparable<T>接口来达到此目的。

IComparable<T>只提供了一个方法:

先看一个例子,这里使用了string,因为string实现了该接口:

其结果是:

string是通过按位字母进行比较的,“a”就小于“b”,所以上述str1应该是小于str2的。

而CompareTo方法返回的是int类型,而比较的结果呢,可能有三种情况:

  • x == y
  • x < y
  • x > y

再通过上面的例子,我们可以看出来:

针对x.CompareTo(y),

  • 如果 x == y,那么 结果 = 0
  • 如果 x < y,那么结果 < 0
  • 如果 x > y,那么结果 > 0

我们可以把代码重构一下,提取出一个低级别方法,便于逻辑复用:

顺便提一下,string并没有实现> < == 等等操作符。

int

所有的原始类型都实现了IComparable<T>。

所以使用上面的方法,也可以比较原始数据类型:

当然这些类型也可以使用操作符,例如:

而string没有实现这些操作符,所以这样写就是错误的:

相等性 vs 比较

直接看图:

其中,针对比较性,System.object并没有支持,因为对于大多数类型而言,对它们的实例进行比较排序是没有意义的。

例如3 < 4,这样就是合理的;而提交按钮 < 取消按钮,这就没有意义了;这个委托 < 另一个委托,这也没有意义。

针对相等性而言,IEquatable<T>仅仅就是对object里的那些Equals方法的补充。而针对比较性而言,IComparable<T>是主打的方式。

其它的方式都有对应。

下面两个黄色的通过”插件的方式“实现的,这里只提一下,不介绍了。

比较性 只比较值

判断相等性的时候,可能判断的是引用相等或者是值相等。

而进行比较排序的时候,其比较的只能是值,因为对引用进行比较排序是没有意义的。

而==和!=操作符可以为原始数据类型和引用类型来使用,而>, <, >=, <= 只能用于原始数据类型。

在自定义类型上实现比较

其实我通常不在我的类型上去实现IComparable<T>,包括引用类型和原始类型。

因为是这样的,比如说有一个Person(人)这个类型,我想对它排序,按照年龄排序,可以;按照姓名排序,也可以;按照身高排序,也可以;但是没有任何一种排序对人来说是最理所当然的。

更好的办法是实现某种比较器。

但是有时候还是需要实现IComparable<T>,那么下面就讲一下怎么做。

值类型

Person Struct:

如果直接使用我们之前的方法,则会报错:

因为它没实现IComparable<T>接口。

使用大于号小于号的话,也会报错:

因为这个类型也没有实现比较操作符。

实现IComparable<T>接口

很简单,直接调用了字段Height的CompareTo方法,因为int类型实现了IComparable<T>接口。

实现比较操作符

一共四个操作符:<, >, <=, >=,必须都得实现。

代码是:

这个很简单就不解释了。

现在代码不会报错了:

其运行结果是:

运行OK了,看似没问题,然后,还有一个问题:

使用等号判断相等性的代码会报错。

如果你不是用==操作符的话,那么代码是没问题的,也是可以进行比较的,也没人强制要求实现==和!=操作符。但是这很奇怪!因为你说 p1 > p2,这个成立,然后再说 p1 != p2这个就编译错误,那就不合理了。

所以,如果你实现了比较操作符,那么相等性操作符也应该一同实现了:

那么既然==和!=都实现了,那么其它的相等性判断方法也应该一同实现:

  • object.Equals()
  • object.GetHashCode()
  • IEquatable<T>

看起来挺麻烦,但这只是一个struct,还是相对简单的。。。。

但针对struct,其实还没完,还有一个非泛型的IComparable接口,泛型出现之前,一直都是用这个接口的。

这个接口现在来说没什么用了,但是如果有其它遗留的老代码需要使用你这个struct,你可能还需要把这个接口实现一下。。。

引用类型

引用类型除了需要考虑上面struct考虑的那些东西外,还需要考虑更多的东西。

首先,需要在CompareTo里面检查是否为null,和类型检查。

而如果Person是一个没有seal的class,那问题就更大了,以前文章里提到的OOP继承问题、类型安全问题、相等性问题将全部出现。因为类型安全和比较性还是没法一起很愉快的工作。反正会很混乱。。。

所以如果事seal的class,那么在其上实现比较性的话还勉强可以接受;否则的话,祝好运。。。

泛型

之前在相等性的文章里,提到过,针对泛型代码来说,==和!=操作符不能很好的工作,而object.Equals()却可以。

这点在比较性里面也是一样的。针对泛型的比较,你需要使用IComparable<T>.CompareTo()方法,而不是比较的操作符>, <, >=, <=等(即使实现了比较操作符)。

如果我把之前的方法代码改成使用比较操作符:

那么就会报错,因为无法约束泛型实现了某些操作符。。。但可以考虑在接口里面实现比较操作符。。。

但是实现比较性的话:

  • 实现IComparable<T>接口
  • 也可选去实现比较操作符。

C# - 实现类型的比较的更多相关文章

  1. 【.net 深呼吸】细说CodeDom(5):类型成员

    前文中,老周已经厚着脸皮介绍了类型的声明,类型里面包含的自然就是类型成员了,故,顺着这个思路,今天咱们就了解一下如何向类型添加成员. 咱们都知道,常见的类型成员,比如字段.属性.方法.事件.表示代码成 ...

  2. 【.net 深呼吸】细说CodeDom(4):类型定义

    上一篇文章中说了命名空间,你猜猜接下来该说啥.是了,命名空间下面就是类型,知道了如何生成命名空间的定义代码,之后就该学会如何声明类型了. CLR的类型通常有这么几种:类.接口.结构.枚举.委托.是这么 ...

  3. opencv中Mat与IplImage,CVMat类型之间转换

    opencv中对图像的处理是最基本的操作,一般的图像类型为IplImage类型,但是当我们对图像进行处理的时候,多数都是对像素矩阵进行处理,所以这三个类型之间的转换会对我们的工作带来便利. Mat类型 ...

  4. [C#] async 的三大返回类型

    async 的三大返回类型 序 博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async 的返回类型为例,单独谈谈. 异步方法具有三个可让开发人员选择的返回类型:Task&l ...

  5. C# - 值类型、引用类型&走出误区,容易错误的说法

    1. 值类型与引用类型小总结 1)对于引用类型的表达式(如一个变量),它的值是一个引用,而非对象. 2)引用就像URL,是允许你访问真实信息的一小片数据. 3)对于值类型的表达式,它的值是实际的数据. ...

  6. salesforce 零基础学习(六十二)获取sObject中类型为Picklist的field values(含record type)

    本篇引用以下三个链接: http://www.tgerm.com/2012/01/recordtype-specific-picklist-values.html?m=1 https://github ...

  7. Dapper逆天入门~强类型,动态类型,多映射,多返回值,增删改查+存储过程+事物案例演示

    Dapper的牛逼就不扯蛋了,答应群友做个入门Demo的,现有园友需要,那么公开分享一下: 完整Demo:http://pan.baidu.com/s/1i3TcEzj 注 意 事 项:http:// ...

  8. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

  9. js:给定两个数组,如何判断他们的相对应下标的元素类型是一样的

    题目: 给Array对象原型上添加一个sameStructureAs方法,该方法接收一个任意类型的参数,要求返回当前数组与传入参数数组(假定是)相对应下标的元素类型是否一致. 假设已经写好了Array ...

  10. C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)

    #include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...

随机推荐

  1. PHP & LAMP & WAMP

    PHP & LAMP & WAMP https://github.com/xgqfrms/DataStructure/issues/7#issuecomment-430538438 h ...

  2. React Native & CodePush & App Center

    React Native & CodePush & App Center https://docs.microsoft.com/en-us/appcenter/distribution ...

  3. nodejs 创建tcp/udp服务器和客户端

    TCP server // https://nodejs.org/api/net.html const net = require("net"); // https://nodej ...

  4. NGK公链有发展前景吗?

    最近网络中经常能看到一个新公链项目NGK的消息,很多朋友也都私下表示过,非常看好今年的NGK.对此,小编对NGK做了一些功课,发觉到NGK未来在商业Dapp应用的发展前景,下面就给大家分享一下我的理解 ...

  5. fork后子进程与父进程的关系

    共享代码空间,各自独立数据空间,子进程初始化数据是父进程的复制. 资料参考: https://blog.csdn.net/u013851082/article/details/76902046

  6. redis5.* 手动构建集群

    1.集群的概念 集群是一组相互独立的.通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理.一个客户与集群相互作用时,集群像是一个独立的服务器.集群配置是用于提高可用性和可缩放性.当 ...

  7. SpringBoot 项目初始化

    工作之余,想要学习一下SpringBoot,通过网络大量教程最终成功运行SpringBoot项目.  第一步 首先,通过教程发现一套完整的快速搭建SpringBoot项目网站:https://star ...

  8. 页面导入导出EXCEL

    引用 using Microsoft.Office.Interop.Excel;using System.Reflection;//反射命名空间using System.IO; protected v ...

  9. git配置了公钥,在下载项目时为什么还要输入密码

    配置git地址:https://www.cnblogs.com/lz0925/p/10794616.html 原文链接:https://blog.csdn.net/xiaomengzi_16/arti ...

  10. 基于3.X版本的脚手架创建VUE项目

    一.基于交互式命令行的方式,创建vue项目 1.命令:vue create 项目名称.项目名称必须是英文的.不要包含中文.特殊的字符和符号.在cmd中输入命令:vue create vue_proje ...