9
10
2015
0

[Effective C#] Item 6: Understand the Relationships Among the Many Different Concepts of Equality


C#提供了四种判断相等性的方法:

先来考虑前两个静态方法。

ReferenceEquals()方法比较传入的两个参数是否引用了同一个对象,并不考虑内容是否相同。需要注意的是如果用这个方法比较值类型,因为对值类型进行了装箱,它将总是返回false,即使是和它自身比较。

Equals()方法则是调用objA.Equals(objB)来判断是否相等。

显然不用重写这两个方法,它们会按预期工作。

如果要自定义对象的相等性,则应重写实例方法Equals()。

在讨论如何重写这个方法之前,我要先说一下相等性应具有的数学性质。

自反性:a=a一定成立;

对称性:若a=b,则一定有b=a;

传递性:若a=b且b=c,则一定有a=c。

在自定义对象的的相等性时,一定要确保符合这三个性质。

现在可以开始讲重点了。

仅当Equals()的默认行为不能满足需求时,才重写这个方法。

Equals()的默认行为和ReferenceEquals()一样。但注意,ValueType重写了这个方法,它比较值类型的所有字段是否相等。

看起来值类型的默认Equals()方法很好用,但是,因为不知道被比较的对象的运行时类型,它是使用反射来实现的,这意味着性能损失。所以,定义值类型时总是应重写Equals()方法。

对于引用类型,仅当你想改变默认的基于引用的相等性时才重定义Equals()。许多BCL中的类都使用了基于值的相等性,例如,两个string对象比较的是它们是否包含了相同的内容。

由于Equals()方法使用了object类型的参数,对值类型进行比较时会进行大量的装箱和拆箱,一般情况下会同时实现IEquatable<T>接口的Equals(T other)。下面是标准的重写Equals()的方式。

注意,Equals()方法不应该抛出异常,如果传入了空引用或类型不符合,应该返回false。

另外如果重写了Equals()方法,同时也应该重写GetHashCode()方法,它将在Item 7中被讨论。

剩下的==操作符就很简单了,定义值类型时,总是应该重写它,同样是因为默认版本使用了反射。对于引用类型,一般不应重写它,BCL中的类预期所有引用类型的==操作符进行基于引用的比较。

最后要提一下IStructuralEquatable<T>接口(《Effective C#》中写的是IStructuralEquality,应该是作者搞错了)中的Equals(),它们比较两个对象在结构上是否相等(一般是集合对象),BCL中的Array和Tuple<>都实现了这个接口。

Category: C#及OOP | Tags: Effective C# | Read Count: 319

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com