C# 中的 ReferenceEquals 方法 - 教程

news/2025/9/23 8:21:02/文章来源:https://www.cnblogs.com/lxjshuju/p/19106511

C# 中的 ReferenceEquals 方法

1. 核心定义与作用

Object.ReferenceEquals 是一个静态方法,它的作用非常纯粹和单一:

判断两个对象引用是否指向内存中的同一个实例(即同一个对象)。

它的方法签名如下:

public static bool ReferenceEquals (object? objA, object? objB);

2. 工作原理与举例说明

让我们通过一系列例子来彻底理解它。

例 1:引用类型的基本行为
class Person
{
public string Name {
get;
set;
}
}
Person p1 = new Person { Name = "Alice"
};
Person p2 = p1;
// p2 是 p1 的引用副本,它们指向同一个对象
Person p3 = new Person { Name = "Alice"
};
// 新对象,内容虽然和 p1 一样,但内存地址不同
Console.WriteLine(Object.ReferenceEquals(p1, p2));
// 输出: True
Console.WriteLine(Object.ReferenceEquals(p1, p3));
// 输出: False
Console.WriteLine(Object.ReferenceEquals(null, null));
// 输出: True (特殊情况)

说明

  • p1p2 指向堆上的同一个 Person 实例,所以 ReferenceEquals 返回 true
  • p1p3 虽然内容相同,但分别是两个不同的对象实例,所以返回 false
  • 两个 null 引用被认为是相等的。
例 2:字符串的特殊情况 - 字符串驻留

字符串 (string) 在 C# 中是不可变的引用类型,但 CLR 使用了一种叫“字符串驻留”的优化技术,这会让 ReferenceEquals 的行为变得有趣。

string s1 = "Hello";
string s2 = "Hello";
// 编译器会进行驻留,s2 和 s1 指向同一个内存地址
string s3 = new string("Hello".ToCharArray());
// 强制在堆上创建一个新的字符串对象
Console.WriteLine(Object.ReferenceEquals(s1, s2));
// 输出: True (因为驻留)
Console.WriteLine(Object.ReferenceEquals(s1, s3));
// 输出: False (不同对象)
// 使用String.Intern方法将s3驻留,之后获取的引用就是驻留池中的引用
string s4 = String.Intern(s3);
Console.WriteLine(Object.ReferenceEquals(s1, s4));
// 输出: True

说明:对于字面量字符串,CLR 会将其放入“驻留池”,所有相同值的字面量都会共享同一个引用,所以 s1s2 是同一个引用。但用 new 等方式创建的字符串对象不会自动驻留。

例 3:值类型的比较 - 装箱

ReferenceEquals 的参数是 object,所以当传递值类型(如 int, struct)时,会发生装箱

int num1 = 10;
int num2 = 10;
// 值类型传递给ReferenceEquals时会被装箱
// num1被装箱到一个新的object实例中
// num2被装箱到另一个新的object实例中
// 两个不同的装箱对象,引用自然不同
Console.WriteLine(Object.ReferenceEquals(num1, num2));
// 输出: False
// 更明显的例子:和自己比较
Console.WriteLine(Object.ReferenceEquals(num1, num1));
// 输出: False 

这是最重要的陷阱!
Object.ReferenceEquals(num1, num1) 也返回 false,因为每次装箱都会产生一个新的临时对象。所以,ReferenceEquals 方法永远不适用于比较值类型,它的结果总是 false(除非比较 null)。

例 4:与 ==Equals 的对比
class Student
{
public string Id {
get;
set;
}
// 假设我们重写了Equals,只比较Id字段
public override bool Equals(object obj) => obj is Student s && Id == s.Id;
// 重写Equals最好也重写GetHashCode
public override int GetHashCode() => Id?.GetHashCode() ?? 0;
}
Student stu1 = new Student { Id = "001"
};
Student stu2 = new Student { Id = "001"
};
Student stu3 = stu1;
Console.WriteLine("ReferenceEquals:");
Console.WriteLine(Object.ReferenceEquals(stu1, stu2));
// False (不同对象)
Console.WriteLine(Object.ReferenceEquals(stu1, stu3));
// True (同一对象)
Console.WriteLine("== Operator:");
// == 默认行为与ReferenceEquals相同,除非被重写
// 假设我们没有重写 == 运算符,所以它执行引用比较
Console.WriteLine(stu1 == stu2);
// False
Console.WriteLine(stu1 == stu3);
// True
Console.WriteLine("Equals Method:");
// Equals 方法被我们重写了,它比较的是Id字段的值
Console.WriteLine(stu1.Equals(stu2));
// True (内容相同)
Console.WriteLine(stu1.Equals(stu3));
// True (内容相同,且是同一对象)

三者的区别总结

方法比较内容可被重写适用于值类型
ReferenceEquals引用地址永远不适用(因装箱)
== 运算符默认是引用地址,但可重写为比较值是(对于内置值类型已重写)
Equals 实例方法默认是引用地址,但通常被重写为比较值是(对于内置值类型已重写)

3. 主要使用场景

既然有 ==Equals,为什么还需要 ReferenceEquals

  1. 进行绝对的引用比较:当你明确地、故意地想知道两个变量是否指向内存中的绝对同一个实例,而不是“值”是否相等时。例如,在实现某些底层基础设施、缓存机制或监听对象身份变化的逻辑时。

  2. 避免被重写逻辑干扰==Equals 都可能被类重写。如果你不信任或不想依赖这些自定义的比较逻辑,ReferenceEquals 提供了一个不可被重写的、最基础的比较方式。

  3. 处理可能为 null 的对象:它是静态方法,即使参数为 null 也不会抛出异常,比直接使用 == 在某些复杂情况下更安全。

// 一个实用的例子:在实现Equals时,先进行引用比较以优化性能
public override bool Equals(object obj)
{
// 如果引用相同,肯定是同一个对象,无需继续比较字段
if (Object.ReferenceEquals(this, obj))
return true;
// 如果对方为null或类型不同,肯定不相等
if (obj is null || this.GetType() != obj.GetType())
return false;
// 最后再进行耗时的字段逐一比较
// ... 比较各个字段的值
}

总结

  • Object.ReferenceEquals 只检查引用是否相同,不检查值。
  • 永远不会用于值类型,因为装箱会产生临时对象,导致比较结果总是 false
  • 对于字符串,要小心字符串驻留带来的影响。
  • 它的主要用途是进行身份识别(Identity Check)而不是值相等性检查,常用于底层实现或需要绕过自定义相等性逻辑的场景。

简单来说,当你问“是同一个东西吗?”时,用 ReferenceEquals;当你问“看起来一样吗?”时,用 Equals==

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/911889.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

phpcms模板行业网站做驾考学时在哪个网站

2023年12月29日,星期五,2023年最后一个工作日了,明天就开始放假元旦了,去年水了31篇,今年到目前为止才水了30篇,所以这篇也水一下来充个数。祝大家元旦快乐,新的一年里越来越好。 被监控主机指…

怎样在网站图片上做店铺广告少儿编程网课平台哪个好

如果是web端的埋点数据,我们可以对这些数据进行分流。 我们可以采用事件分流,步骤如下 定义事件类型: 根据埋点数据的内容,定义不同的事件类型。例如,可以有页面访问事件、按钮点击事件、表单提交事件等。 提取关键信…

合肥网站搜索引擎优化西安市建设局官方网站

文章目录 解决方案1. 表空间管理2. 分区表3. 自动扩展配置4. 监控和告警5. 使用外部工具和服务 示例代码示例1:创建表空间示例2:创建分区表示例3:调整配置参数示例4:使用监控和告警工具 总结 在PostgreSQL中,随着数据的…

医院网站制作设计深圳报业集团官网

接上一篇:实战04_redis-cluster集群搭建https://blog.csdn.net/weixin_40816738/article/details/100635263 下一篇:实战_06_SpringBoot整合edis-cluster集群版本https://blog.csdn.net/weixin_40816738/article/details/100658669

网站建设技术网站建设wordpress可以移动端

1.模板数据的调用 一张图了解一下在wxml页调用预先定义好的模板: 可以看到上面调用了两个模板,数据调用却是不同的,obj是一个对象,对象内包含多个键值对形式的数据; tabbar是一个一维数组,每个数组项又都是…

【一周AI资讯】Claude自动抓取网页;美团发布生活Agent;阿里通义发布双模型 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

安庆市住房和城乡建设局网站首页全包圆装修400电话怎么打

文章目录 ControlNet的介绍及安装ControlNet的介绍ControlNet的安装 ControlNet的功能介绍ControlNet的应用与演示 ControlNet的介绍及安装 ControlNet的介绍 ControlNet 的中文就是控制网,本质上是Stable Diffusion的一个扩展插件,在2023年2月份由斯坦…

Vue2 父子组件传值(简化版示例) - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

怎样在网站上做推广已备案网站注册

零基础微服务架构理论入门介绍 一个基于分布式的服务架构应该含有的架构内容如下: 1.1SpringCloud是什么 1.2基于微服务的系统 1.3服务与之对用的技术 1.4课程目录 第一章_SpringBoot和SpringCloud版本选择 详细可见SpringCloud2020.mmap文件 1.概述 2.SpringBoo…

网站建设月总结建设网站基本步骤

Redis 【1】—— 安装 与 配置 一、安装 与 配置(一)使用 yum 安装(二)创建符号链接1. 软链接2. 相关指令 (三)修改配置文件(四)Redis 的启停 一、安装 与 配置 (一&…

用记事本做电影介绍的网站佛山建筑设计院有限公司官网

超微服务器启动项设置 内容精选换一换微服务部署完后,您可以根据微服务的运行情况进行微服务的治理。您可以先在“服务目录 > 微服务列表”中创建微服务,启动微服务后,根据yaml文件的配置,会在对应的服务下注册服务实例。如果没…

ajax网站开发典型实例网站中flash怎么做

Union与Union All的区别如果我们需要将两个select语句的结果作为一个整体显示出来,我们就需要用到union或者union all关键字。union(或称为联合)的作用是将多个结果合并在一起显示出来。union和union all的区别是,union会自动压缩多个结果集合中的重复结果&#xff…

广州宝安建网站在建设部网站如何查询注册信息

经过六年的迭代与沉淀,腾讯Tencent Server Web (以下简称TSW)这一公司级运维组件于今日正式开源。TSW是面向WEB前端开发者,以提升问题定位效率为初衷,提供云抓包、全息日志和异常发现的Node.js基础设施。TSW每天为百亿次请求提供稳定服务&…

七冶建设集团网站苏醒8 WordPress

a ⃗ \vec{a} a 向量 a ‾ \overline{a} a 平均值 a ‾ \underline{a} a​下横线 a ^ \widehat{a} a (线性回归,直线方程) y尖 a ~ \widetilde{a} a 颚化符号 等价无穷小 a ˙ \dot{a} a˙ 一阶导数 a \ddot{a} a 二阶导数 $\vec{a}$ 向量 $\overline{a}$ …

遇到一件循环导入事件

遇到一件循环导入事件 #pythonProject1\api\__init__.py from flask import Flask from flask_sqlalchemy import SQLAlchemy from api.config.config import Config_dict from api.modules.auth import auth_blu app.…

电子商务型网站溧水114网站开发

loguru 是一个现代的 Python 日志记录库,它旨在提供比标准 logging 模块更简洁、强大和灵活的体验。loguru 通过简化配置、提升性能和增加一些高级功能,如日志文件的自动滚动、颜色化的终端输出等,使得日志处理变得更加高效和愉快。 1、安装 loguru 通过 pip 进行安装: …

flask实现后端接口的封装和开发部分

flask:实现后端接口的封装和开发部分 HTTP 请求方法和响应 对照后面的代码案例,我们看看 Flask-Restful 是如何去发起请求并处理响应的。 from flask import Flask, request from flask_restful import Api, Resource…

怎么自己弄一个网站免费网页代理在线

文章目录 一、前言二、库三、线程四、定时器五、订阅/发布5.1 回调函数5.2 堵塞等待一、前言 教程目录大纲请查阅:玩转合宙Luat教程——导读 写一写Lua程序基础的东西。 包括如何调用库,如何创建线程、如何创建定时器,如何使用订阅/发布事件。 二、库 程序从main.lua开始通…

深色网站江阴网站制作

文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道(云端设置)2.3.Cpolar稳定隧道(本地设置) 3. 公网访问测试总结 前言 博客作为使…

做网站需要准备的资料全网seo

Flutter 是一款强大的跨平台移动应用框架,它提供了丰富的组件和功能,以便开发者构建高性能、美观的移动应用。其中,GestureDetector 是一个非常重要的手势识别组件,用于捕获用户在屏幕上的各种手势操作。在本文中,我们…