引用相等性和值相等性

在 C# 中,相等性分为引用相等性值相等性。引用相等性是指,若两个引用类型的变量引用的是同一个对象,则它们具有引用相等性。
// x, y, z 都是引用类型变量
object x = new object();
object y = new object();
object z = x; // 输出 false,x 和 y 不具有引用相等性
Console.WriteLine(object.ReferenceEquals(x, y)); // 输出 true,x 和 z 具有引用相等性
Console.WriteLine(object.ReferenceEquals(x, z)); // 输出 true,x 和自身具有引用相等性
Console.WriteLine(object.ReferenceEquals(x, x));
值相等性是指,若两个变量----可以是引用类型或值类型----包含语义上相同的值,则它们具有值相等性。
int i = ;
int j = ;
int k = ; // 输出 false,i 和 j 不具有值相等性
Console.WriteLine(i.Equals(j)); // 输出 true,i 和 k 具有值相等性
Console.WriteLine(i.Equals(k)); // 输出 true,i 和自身具有值相等性
Console.WriteLine(i.Equals(i));

== 和 != 操作符

== 操作符用于判断两个变量是否相等,默认的实现是:
  • 对于字符串以外的引用类型,判断变量是否具有引用相等性
  • 对于值类型,判断变量是否具有值相等性
  • 对于字符串类型,判断变量是否具有值相等性
!= 操作符与 == 操作符含义相反 。
 
C#允许重载 == 和 != 操作符。规则是,若要重载其中的一个,则必须重载另一个。
 

Object.ReferenceEquals 静态方法

Object.ReferenceEquals 静态方法用于判断引用相等性,它接受两个 object 类型的参数:
 
int i = ;

// 输出 false,i 被装箱两次,是两个不同的对象,不具有引用相等性
Console.WriteLine(object.ReferenceEquals(i, i)); // 相当于:
object x = i;
object y = i;
Console.WriteLine(object.ReferenceEquals(x, y));

.net 框架的源码实现中,ReferenceEquals 方法使用 == 操作符进行判断:

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[System.Runtime.Versioning.NonVersionable]
public static bool ReferenceEquals (Object objA, Object objB) {
return objA == objB;
}

代码参考:http://referencesource.microsoft.com/#mscorlib/system/object.cs,4d607d6d56a93c7e

字符串相等性

在 .net 中,字符串是引用类型,但 == 运算符和 Equals 方法都使用值相等性对字符串进行比较。默认的比较方式是区分大小写的,有时我们希望进行不区分大小写的比较。因此 String 类还有一个 Equals 方法的重载,允许指定如何对字符串进行相等性比较:

public bool Equals(string value, StringComparison comparisonType)

StringComparison 参数允许指定要使用的区域性、大小写和排序规则。

字符串的例子提示相等性比较不是绝对的,两个相同的对象,可能在一个上下文中相等,而在另一个上下文中不相等。

Object.Equals 实例方法

Object.Equals 实例方法的签名是:
public virtual bool Equals(Object obj)

这是一个虚方法,具体行为取决于是否进行了重写。Object 中 Equals 方法的实现是,如果参数 obj 与当前对象是同一实例,则返回 true。

ValueType 重写了 Equals 方法:若两个值类型变量的类型相同,并且所有实例字段相等,则它们具有值相等性。ValueType 中的 Equals 方法使用反射实现,对性能略有影响。对于自定义值类型,可考虑重写 Equals 方法避免反射带来的性能损失。

Object.Equals 静态方法

这是实用工具性质的方法,用于判断两个变量是否相等,方法签名是:

public static bool Equals(Object objA, Object objB)
它的实现代码如下:
public static bool Equals(Object objA, Object objB)
{
if (objA == objB)
{
return true;
}
if (objA == null || objB == null)
{
return false;
}
return objA.Equals(objB);
}
 
分析:
1,若两个变量引用相同的对象,或都为 null,则返回 true。
2,若任意一个变量为 null,另一个不为 null,则返回 false。
3,若两个变量都不为 null,并且不引用同一对象,那么返回 objA.Equals(objB) 的结果。
 

重写 Object.Equals 实例方法

重写 Object.Equals 实例方法时,应遵循以下规则:

  • 自反:x.Equals(x) 应返回 true
  • 对称:x.Equals(y) 和 y.Equals(x) 应具有相同的结果
  • 传递:如果 x.Equals(y) 为 true,并且 y.Equals(z) 为 true,那么 x.Equals(z) 应为 true
  • 稳定:如果 x 和 y 未修改,那么连续调用 x.Equals(y) 应具有相同的结果
  • 当实参为 null 时,应始终返回 null。
  • 若重写 Equals 方法,那么还应重写 GetHashCode 方法,并且,两个相等对象的 GetHashCode 方法应返回相同的值

IEquatable<T> 泛型接口

IEquatable<T> 定义类型特定的相等性比较方法,此接口只定义了一个方法,签名为:

bool Equals(T other)

此接口是从 .net 2.0 开始伴随泛型集合引入的。在泛型集合上调用 Contains、IndexOf、LastIndexOf 和 Remove 等方法时,将使用此接口进行相等性比较。根据 MSDN 的说明,任何可能存储在泛型集合中的类都应实现此接口。

如果实现了 IEquatable 接口,还应重写 Object.Equals 实例方法和 Object.GetHashCode 方法。

IEqualityComparer<T> 泛型接口和 EqualityComparer<T> 泛型类

IEqualityComparer 声明了两个方法:

bool Equals(T x, T y)
int GetHashCode(T obj)

此接口允许在泛型集合中跳过 T 本身的 Equals 和 GetHashCode 实现,如果向泛型集合提供了 IEqualityComparer<T> 的实例,那么集合将使用 IEqualityComparer<T> 的 Equals 和 GetHashCode 方法进行比较。

EqualityComparer<T> 为 IEqualityComparer<T> 泛型接口的实现提供基类。EqualityComparer<T>.Default 属性返回默认的相等性比较器,IEquatable<T> 与泛型集合的配合实际上是在这里起作用的:

  • 若 T 实现了 IEquatable<T> 接口,则 EqualityComparer<T>.Default 使用 IEquatable<T> 接口进行相等性比较
  • 否则,EqualityComparer<T>.Default 使用 Object.Equals 进行相等性比较
 
 

.net 中的相等性比较的更多相关文章

  1. spring中的传播性 个人认为就是对方法的设置 其作用能传播到里面包含的方法上

    spring中的传播性 个人认为就是对方法的设置 其作用能传播到里面包含的方法上

  2. 如何在Django模型中管理并发性 orm select_for_update

    如何在Django模型中管理并发性 为单用户服务的桌面系统的日子已经过去了 - 网络应用程序现在正在为数百万用户提供服务,许多用户出现了广泛的新问题 - 并发问题. 在本文中,我将介绍在Django模 ...

  3. 【软件构造】第三章第五节 ADT和OOP中的等价性

    第三章第五节 ADT和OOP中的等价性 在很多场景下,需要判定两个对象是否 “相等”,例如:判断某个Collection 中是否包含特定元素. ==和equals()有和区别?如何为自定义 ADT正确 ...

  4. Flume-NG中Transaction并发性探究

    我们曾经在Flume-NG中的Channel与Transaction关系(原创)这篇文章中说了channel和Transaction的关系,但是在source和sink中都会使用Transaction ...

  5. 探究CSS中的包裹性

    之前一直都知道css中的部分元素具有包裹性,今天写博客的时候正好也遇到了一个,所以想总结一下,有错误的地方欢迎指出来. 什么是包裹性? 包裹性就是父元素的宽度会收缩到和内部元素宽度一样. 哪些元素具有 ...

  6. css中的层叠性及权重的比较

    假如同一个标签被多个选择器选中,每个选择器都设置了相同的样式,浏览器中加载时这个样式听谁的? 不同选择器设置的同一个样式,只会选择一个进行加载,不会叠加. 为了解决听谁的问题,引入层叠性的概念. 层叠 ...

  7. 前端设计师如何提高UI界面中的阅读性

    阅读体验是ui设计中必不可少的一项,良好的设计应该都是可读的设计,如果信息都无法正常而清晰的传达,那么设计就失去了意义.设计的可读性和排版设计息息相关,这也就跟设计师的设计功底息息相关.下面简单介绍文 ...

  8. JavaScript中各存在性函数

    JavaScript中有很多表示存在性和从属关系的函数,本文介绍如下几个: 1)有关实例与构造函数原型之间的关系:isPrototypeOf(),Object.getPrototypeOf(); 2) ...

  9. JS中的相等性判断===, ==, Object.is()

    首发地址:http://www.geeee.top/2019/11/15/equality-comparisons/,转载请注明出处 相信刚接触JS的人都会被他的想等性判断给整糊涂,看看下面代码,你能 ...

随机推荐

  1. 关于微信小程序拒绝授权后,重新授权并获取用户信息

    最近公司做了一些有关微信小程序的项目,涉及到授权获取用户基本信息,但是在拒绝授权之后就不会再出现授权窗口: 看网上也有很多人遇到了同样的问题,所以记录下来我的处理方法,供大家和自己学习和记录: 当调用 ...

  2. [c++]大数运算1---利用C++ string实现任意长度正小数、整数之间的加减法

    一.概述 本文属于大大维原创,未经笔者本人允许,严禁转载!!! C/C++中的int类型能表示的范围是-2E31-2E31–1.unsigned类型能表示的范围是0-2E32–1,即 0-429496 ...

  3. 模板C++ 03图论算法 2最短路之全源最短路(Floyd)

    3.2最短路之全源最短路(Floyd) 这个算法用于求所有点对的最短距离.比调用n次SPFA的优点在于代码简单,时间复杂度为O(n^3).[无法计算含有负环的图] 依次扫描每一点(k),并以该点作为中 ...

  4. es6之各种数据类型的扩展

    一. 字符串的扩展 为字符串添加了Iterator,可以被for...of遍历 includes.startsWith.endsWith都会返回布尔值,且支持第二个参数(开始搜索的位置),endsWi ...

  5. APUE-文件和目录(六)函数ftw和nftw

    名字 ftw,nftw - 文件树遍历 概要 #include <ftw.h> int nftw(const char *dirpath, int (*fn) (const char *f ...

  6. java小白进阶安卓第一天

  7. Chapter 5:Spectral-Subtractive Algorithms

    作者:桂. 时间:2017-05-24  10:06:39 主要是<Speech enhancement: theory and practice>的读书笔记,全部内容可以点击这里. 书中 ...

  8. Netty方法误解ChannelHandlerContext.writeAndFlush(Object msg)

    乍一看这个方法,以为什么消息都能输出,因为参数是Object类型的,但实际上,netty内部只支持两种类型,如图

  9. Java使用条件语句和循环结构确定控制流

    与任何程序设计语言一样,Java使用条件语句和循环结构确定控制流.本文将简单讲解条件.循环和switch. 一.块作用域 块(block),即复合语句.是指由一对大括号括起来的若干条简单的Java语句 ...

  10. tomcat之 Tomcat 7.0.78 单机多实例配置

    前言:JDK(JavaDevelopment Kit)是Sun Microsystems针对Java开发员的产品.自从Java推出以来,JDK已经成为使用最广泛的javaSDK. JDK是整个Java ...