c# 高级编程 6章133页 【比较对象的相等性】

对象相等的机制,有所不同,取决于是引用类型还是值类型


比较引用类型的相等性

System.Object定义了3个不同的方法来比较对象的相等性:

  1. ReferenceEquals()
  2. 可以被重写的虚拟实例方法:Equals()
  3. 静态方法:Equals()

外加可以以下途径:

  1. 实现接口IEquality<T>
  2. 比较运算符 ==

总共有上述5种方法。


1. ReferenceEquals()

  • 是静态方法 (静态,所以不能被重写)
  • 是看两个引用是否在内存中有相同的地址(或者说是看否为同一个实例)
  • object.ReferenceEquals(null, null),会返回true
        private static void ReferenceEqualsSample()
        {
            SomeClass x = new SomeClass(), y = new SomeClass(), z = x;

            bool b1 = object.ReferenceEquals(null, null); // returns true
            bool b2 = object.ReferenceEquals(null, x);    // returns false
            bool b3 = object.ReferenceEquals(x, y);       // returns false because x and y
                                                          // reference different objects
            bool b4 = object.ReferenceEquals(x, z);       // references the same object
        }
复制代码

2. 可以被重写的虚拟实例方法:Equals()

  • System.Object的这个虚拟实例方法Equals() 比较的是引用(也就是看是否为同一个实例)。
  • 但是,可以在System.Object的子类(也就是任何类)里面重写这个Equals()方法,让它按某个值来比较对象实例(而不是直接比较对象实例的引用)。

这里有一个形象的使用场景示例:

  • 类的实例做字典的键 (键不能重复,所以字典的实现里会调用Equals()方法来比较键的相等性)。这时,就可以根据需要,重写Equals()方法,让作为键的实例实际上按照某个成员的值来比较相等性

  • 这里重写Equals() 方法,需要特别注意,重写的代码不应该抛出异常。否则调用这个Equals方法的.NET基类,例如字典类就可能会出问题。

注意: 当字典的键为类实例时,相等性比较,其实也可以用重写Object.GetHashCode()的方法来做,但是和重写Equals()方法相比,重写Object.GetHashCode()工作效率比较低。


3. 静态Equals()

  • Equals()的静态版本和虚实例版本作用相同
  • 静态版带两个参数

它的工作原理如下:

  • 两个参数,如果都是null, 则返回true;
  • 有一个参数是null, 另一个不是null, 则返回false;
  • 两个参数都不是null, 则调用Equals()的虚实例版本来进一步比较 (因此如果重写了Equals()的虚实例版本,也就等同于重写了Equals()的静态版本)

5. 比较运算符 ==

  • 比较运算符==, 比较的是引用
  • 但为了直观,一些类会重写这个运算符==,以值来做相等性比较。例如System.String就重写了这个运算符==,会比较字符串的内容,而不是比较引用

比较值类型的相等性

  1. System.ValueType类中重载了的实例方法Equals()
  2. 没有实际意义但存在的ReferenceEquals()

1. System.ValueType类中重载了的实例方法Equals()

  • 比较的是值
  • 如果是struct, sA.Equals(sB) 会挨个比较两者的所有字段,看它们的值是否相同
  • 如果是struct(且字段里有引用类型),会挨个比较两者的所有字段,遇到引用类型的字段,只比较引用,这时,如果只比较引用类型字段的引用不符合需求的话,可以考虑重写这个Equals()方法,让它按照合适的值进行相等性比较。

2. 没有实际意义但存在的ReferenceEquals()

  • 比较的是值类型的引用
  • 但,值类型要先装箱,才能转换成引用,才能用ReferenceEquals()
  • 装箱是单独装箱,这意味着会得到不同的引用
  • 所以ReferenceEquals()用于值类型时,总是返回false
bool b = ReferenceEquals(v, v); 
复制代码

猜你喜欢

转载自juejin.im/post/7016680163018539021