一次性搞懂C#中的==、Equals()和ReferenceEquals()的区别
首先看CLR中基本值类型之间的比较,先看代码:
int age1 = 30; int age2 = 30; Console.WriteLine("int == int: {0}", age1 == age2); Console.WriteLine("int == int: {0}", age2 == age1); Console.WriteLine("int Equals int: {0}", age1.Equals(age2)); Console.WriteLine("int Equals int: {0}", age2.Equals(age1)); Console.WriteLine("int ReferenceEquals int: {0}", object.ReferenceEquals(age1, age2)); Console.WriteLine("int ReferenceEquals int: {0}", object.ReferenceEquals(age2, age1)); Console.ReadLine();
运行结果:
对于相同的基本值类型(上述示例代码中都是int),==和Equals()比较结果是一样的;由于ReferenceEquals()是判断两个对象的引用是否相等,对于值类型,因为每次判断前都必须进行装箱操作,也就是每次都生成了一个临时的object,因而永远返回false。下面我把代码里面的age2的类型改为byte型,比较结果有什么变化呢?请看运行结果:
现在我们发现,age1.Equals(age2)和age2.Equals(age1)结果不一样。在基本值类型的比较中,==比较的是"值"的内容,如果两个对象的"值"一样,则两个对象是"=="的;但是Equals()做的事要稍微多一点,在Equal()中其实还存在着一个"隐式转换"的过程,也就是说上面代码中的age1.Equals(age2)相当于int.Equals(int),byte型数据可以隐式转换为int型数据,所以age1.Equals(age2)结果为true;而age2.Equals(age1)相当于byte.Equals(byte),但是int型数据不能隐式转成byte型,因为存在数据精度丢失的可能。其实,age2.Equals(age1)的Equals()应该类似与下面的代码:
public override bool Equals(object obj) { if (!(obj is Byte)) { return false; } return m_value == ((Byte)obj).m_value; }
如果是显式转换,age2.Equals((byte)age1)这个时候结果就是true了。
下面说一下字符串string类型之间的比较,字符串是特殊的引用类型,因为它是"不可变"的。先看代码:
string name1 = "Jack"; string name2 = "Jack"; object o1 = name1; object o2 = name2; Console.WriteLine("name1 == name2: {0}", name1 == name2); Console.WriteLine("name1 Equals name2: {0}", name1.Equals(name2)); Console.WriteLine("o1 == o2: {0}", o1 == o2); Console.WriteLine("o1 Equals o2: {0}", o1.Equals(o2)); Console.WriteLine("o1 == name2: {0}", o1 == name2); Console.WriteLine("o1 Equals name2: {0}", o1.Equals(name2)); Console.WriteLine("name1 ReferenceEquals name2: {0}", object.ReferenceEquals(name1, name2)); Console.WriteLine("o1 ReferenceEquals o2: {0}", object.ReferenceEquals(o1, o2)); Console.ReadLine();
上述代码运行结果:
比较结果全部都是true,现在逐一讲解。有人会说name1和name2存储的都是"Jack",所以name1和name2其实就是同一个对象,所以name1==name2和name1.Equals(name2)的比较结果是一样的;也许你是对的。我们通过.NET Reflector工具查看string的源码,会看到其中的这一段代码:
操作符==其实就是返回了Equals()而已。所以对于为什么name1==name2和name1.Equals(name2)的比较结果是一样的解释,我觉得这个解释比"name1和name2其实就是同一个对象"说法更直观一些。
我们知道,由于string类型的特殊性,CLR可以通过一个string对象共享多个完全一致的string内容,所以上面的name1和name2指向的地方是一样的,下面的o1 == o2、o1 == name2、object.ReferenceEquals(name1, name2)的比较结果都是true也验证了这个说法(其实object.ReferenceEquals(name1, o2)也是true)。但是,如果把name1和name2的赋值变成这样呢?
string name1 = new string(new char[] { 'J', 'a', 'c', 'k' }); string name2 = new string(new char[] { 'J', 'a', 'c', 'k' });
看运行结果:
name1==name2和name1.Equals(name2)的比较结果一样好理解,就像上面说的,操作符==其实就是返回了Equals()而已(对于引用类型,Equals()比较的都是托管堆上存储的内容),所以二者结果一样。但是object对象o1和o2的比较,o1 == o2和o1.Equals(o2)结果不一样了。object对象的==比较的是类型对象指针,o1和o2是两个object,二者的类型对象指针必然不同;Equals()比较的是o1和o2在托管堆上存储的内容,故o1.Equals(o2)为true。这也说明了下面的o1 == name2为false和o1.Equals(name2)为true了。
我们先看一下object.ReferenceEquals内部的代码:
现在对于object.ReferenceEquals(name1, name2)和object.ReferenceEquals(o1, o2)结果都是false应该很好理解了,其实就是两个object的==问题!
最后说一下自定义引用类型的比较。
class MyName { private string _id; public string Id { get { return _id; } set { _id = value; } } public MyName(string id) { this.Id = id; } }
把上面name1和name2的声明改为:
MyName name1 = new MyName("12"); MyName name2 = new MyName("12");
其他不变,运行结果:
name1和name2是截然不同的两个对象,比较结果全部为false应该很好理解了。
更多一次性搞懂C#中的==、Equals()和ReferenceEquals()的区别相关文章请关注PHP中文网!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

C语言中通过转义序列处理特殊字符,如:\n表示换行符。\t表示制表符。使用转义序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要转义两次。不同平台和编译器可能有不同的转义序列,请查阅文档。

在 C 语言中,char 类型在字符串中用于:1. 存储单个字符;2. 使用数组表示字符串并以 null 终止符结束;3. 通过字符串操作函数进行操作;4. 从键盘读取或输出字符串。

C 语言中符号的使用方法涵盖算术、赋值、条件、逻辑、位运算符等。算术运算符用于基本数学运算,赋值运算符用于赋值和加减乘除赋值,条件运算符用于根据条件执行不同操作,逻辑运算符用于逻辑操作,位运算符用于位级操作,特殊常量用于表示空指针、文件结束标记和非数字值。

在 C 语言中,char 和 wchar_t 的主要区别在于字符编码:char 使用 ASCII 或扩展 ASCII,wchar_t 使用 Unicode;char 占用 1-2 个字节,wchar_t 占用 2-4 个字节;char 适用于英语文本,wchar_t 适用于多语言文本;char 广泛支持,wchar_t 依赖于编译器和操作系统是否支持 Unicode;char 的字符范围受限,wchar_t 的字符范围更大,并使用专门的函数进行算术运算。

多线程和异步的区别在于,多线程同时执行多个线程,而异步在不阻塞当前线程的情况下执行操作。多线程用于计算密集型任务,而异步用于用户交互操作。多线程的优势是提高计算性能,异步的优势是不阻塞 UI 线程。选择多线程还是异步取决于任务性质:计算密集型任务使用多线程,与外部资源交互且需要保持 UI 响应的任务使用异步。

在 C 语言中,char 类型转换可以通过:强制类型转换:使用强制类型转换符将一种类型的数据直接转换为另一种类型。自动类型转换:当一种类型的数据可以容纳另一种类型的值时,编译器自动进行转换。

char 数组在 C 语言中存储字符序列,声明为 char array_name[size]。访问元素通过下标运算符,元素以空终止符 '\0' 结尾,用于表示字符串终点。C 语言提供多种字符串操作函数,如 strlen()、strcpy()、strcat() 和 strcmp()。

C语言中没有内置求和函数,需自行编写。可通过遍历数组并累加元素实现求和:循环版本:使用for循环和数组长度计算求和。指针版本:使用指针指向数组元素,通过自增指针遍历高效求和。动态分配数组版本:动态分配数组并自行管理内存,确保释放已分配内存以防止内存泄漏。
