Effective C#原则9:明白几个相等运算之间的关系

明白ReferenceEquals(), static Equals(), instance Equals(), 和运算行 符==之间的关系。

当你创建你自己的类型时(不管是类还是结构),你要 定义类型在什么情况下是相等的。C#提供了4个不同的方法来断定两个对象是否 是相等的:

public static bool ReferenceEquals
 ( object left, object right );
public static bool Equals
 ( object left, object right );
public virtual bool Equals( object right);
public static bool operator==( MyClass left, MyClass right );

这种语言让你可以为上面所有的4种方法创建自己的版 本。But just because you can doesn't mean that you should.你或许从 来不用重新定义前面两个方法。你经常遇到的是创建你自己实例的Equals()方法 ,来为你的类型定义语义;或者你偶而重载运==运算符,但这只是为了考虑值类 型的性能。幸运的是,这4个方法的关系,当你改变其中一个时,会影响到其它 的几个。是的,须要4个方法来完整的测试对象是否完全相等。但你不用担心, 你可以简单的搞定它们。

和C#里其它大多数复杂元素一样,这个(对相等 的比较运算)也遵守这样的一个事实:C#充许你同时创建值类型和引用类型。两 个引用类型的变量在引用同一个对象时,它们是相等的,就像引用到对象的ID一 样。两个值类型的变量在它们的类型和内容都是相同时,它们应该是相等的。这 就是为什么相等测试要这么多方法了。

我们先从两个你可能从来不会修 改的方法开始。Object.ReferenceEquals()在两个变量引用到同一个对象时返回 true,也就是两个变量具有相同的对象ID。不管比较的类型是引用类型还是值类 型的,这个方法总是检测对象ID,而不是对象内容。是的,这就是说当你测试两 个值类型是否相等时,ReferenceEquals()总会返回false,即使你是比较同一个 值类型对象,它也会返回false。这里有两个装箱,会在原则16中讨论。(译注: 因为参数要求两个引用对象,所以用两个值类型来调用该方法,会先使两个参数 都装箱,这样一来,两个引用 对象自然就不相等了。)

int i = 5;
int j = 5;
if ( Object.ReferenceEquals( i, j ))
  Console.WriteLine( "Never happens." );
else
  Console.WriteLine( "Always happens." );
if ( Object.ReferenceEquals( i, i ))
 Console.WriteLine( "Never happens." );
else
 Console.WriteLine( "Always happens." );

你或许决不会重新定义 Object.ReferenceEquals(),这是因为它已经确实实现了它自己的功能:检测两 个变量的对象ID(是否相同)。

第二个可能从来不会重新定义的方法是静 态的Object.Equals()。这个方法在你不清楚两个参数的运行类型时什么时,检 测它们是否相等。记住:C#里System.Object是一切内容的最终基类。任何时候 你在比较两个变量时,它们都是System.Object的实例。因此,在不知道它们的 类型时,而等式的改变又是依懒于类型的,这个方法是怎样来比较两个变量是否 相等的呢?答案很简单:这个方法把比较的职责委交给了其中一个正在比较的类 型。静态的Object.Equals()方法是像下面这样实现的:

public static bool Equals( object left, object right )
{
 // Check object identity
 if (left == right )
  return true;
 // both null references handled above
 if ((left == null) || (right == null))
  return false;
 return left.Equals (right);
}

时间: 2024-09-10 10:29:48

Effective C#原则9:明白几个相等运算之间的关系的相关文章

Effective C#原则27:避免使用ICloneable

ICloneable看上去是个不错的主意:为一个类型实现ICloneable接口后就可 以支持拷贝了.如果你不想支持拷贝,就不要实现它. 但你的对象并不 是在一个"真空"的环境中运行,但考虑到对派生类的些影响,最好 还是对ICloneable支持.一但某个类型支持ICloneable, 那么所有的派生类都必 须保持一致,也就是所有的成员必须支持ICloneable接口或者提供一种机制支持 拷贝.最后,支持深拷贝的对象,在创建设计时如果包含有网络结构的对象,会 使拷贝很成问题.IClon

Effective C#原则18:实现标准的处理(Dispose)模式

我们已经讨论过,处理一个占用了非托管资源对象是很重要的.现在是时候 来讨论如何写代码来管理这些类占用的非内存资源了.一个标准的模式就是利用 .Net框架提供的方法处理非内存资源.你的用户也希望你遵守这个标准的模式.也就是通过实现IDisposable接口来释放非托管的资源,当然是在用户记得调用 它的时候,但如果用户忘记了,析构函数也会被动的执行.它是和垃圾回收器一 起工作的,确保在一些必要时候,你的对象只会受到因析构函数而造成的性能损 失.这正是管理非托管资源的好方法,因此有必要彻底的弄明白它.

9个原则设计网站着陆页 增进与用户的关系

我儿子三四岁的时候,有天下午外面下雨,于是我把他带到一个巨大的室内海边游乐场玩.我以为那些闪烁的灯光,令人眼花缭乱的机器摆动和大堆青蛙过河.小蜜蜂.大金刚之类的模型会让他很兴奋,让我们在那里度过几小时的快乐亲子时间. 可实际上,他却踌躇地呆立在游乐场正中,眼花缭乱,不知所措.随便朝一个斜坡扔了几个小球后,他转过头问我:"妈妈,够了吗?我们可以回家了吗?" 网站的着陆页面时常看起来感觉就像游乐场,对访问网站的用户不仅没有吸引力和引导效果,反而令他们丈二和尚摸不着头脑--这也就意味着你的访

Effective C#原则10:明白GetHashCode()的缺陷

这是本书中唯一一个被一整个函数占用的原则,你应该避免写这样的函数. GetHashCode()仅在一种情况下使用:那就是对象被用于基于散列的集合的关键 词,如经典的HashTable或者Dictionary容器.这很不错,由于在基类上实现的 GetHashCode()存在大量的问题.对于引用类型,它可以工作,但高效不高:对 于值类型,基类的实现经常出错.这更糟糕.你自己完全可以写一个即高效又正 确的GetHashCode().没有那个单一的函数比GetHashCode()讨论的更多,且令人 困惑

Effective C#原则50:了解ECMA标准

ECMA标准是C#语言所有功能的官方说明.ECMA-334定义了C#语言1.0的标准, 你可以从The C# Programming Language这本书上学习C#2.0的计划(译注:现在 已经不是计划了),这本书的作者是Anders Hejlsberg, Scott Wiltamuth, 和 Peter Golde (Addison-Wesley, 2003).这本书是一个语言手册,而不是指南. 它详细说明了这门语言书面定义的每一个功能.每一种语言都只一种标记,可以 让你更加明白每一种语言的

Effective C#原则47:选择安全的代码

.Net运行时已经设计好了,一些怀有恶意的代码不能渗透到远程计算机上并 执行.目前一些分部式系统依懒于从远程机器上下载和执行代码.如果你可以通 过Internet或者以太网来发布你的软件,或者直接从web上运行,但你须要明白 CRL在你的程序集中的一些限制.如果CLR不是完全相信一个程序集,它会限制一 些的行为.这些调用代码要有访问安全认证(CAS).从另一方面来说,CLR强制要 求基于角色的安全认证,这样这些代码才能或者不能在基于一个特殊的角色帐号 下运行. 安全违例是运行时条件,编译器不能强

Effective C#原则41:选择DataSet而不是自定义的数据结构

因为两个原则,把DataSet的名声搞的不好.首先就是使用XML序列化的 DataSet与其它的非.Net代码进行交互时不方便.如果在Web服务的API中使用 DataSet时,在与其它没有使用.Net框架的系统进行交互时会相当困难.其次, 它是一个很一般的容器.你可以通过欺骗.Net框架里的一些安全类型来错误 DataSet.但在现代软件系统中,DataSet还可以解决很多常规的问题.如果你明 白它的优势,避免它的缺点,你就可以扩展这个类型了. DataSet类设计 出来是为了离线使用一些存储

Effective C#原则37:使用标准的配置机制

我们要寻求一种避免直接写代码的应用程序配置和信息设置方法,我们已经 创建了多种不同的策略来存储配置信息.而我们是要寻求一种正确的方法,我们 要不断提高和改我们的想法,关于哪里是放置这些信息的好地方.INI文件?这 是Windows3.1做的事,配置信息的结构是受限制的,而且在文件名上可能还会与 其它程序程序相冲突.注册表?是的,是这个正确的想法,但它也有它的限制. 乱七八糟的程序可能会通过在注册表里写一些错误信息来严重破坏计算机.正因 为写注册表存在危险,一个应用程序必须有管理员权限来写注册表的

Effective C#原则23:避免返回内部类对象的引用

你已经知道,所谓的只读属性就是指调用者无法修改这个属性.不幸运的是 ,这并不是一直有效的.如果你创建了一个属性,它返回一个引用类型,那么调 用者就可以访问这个对象的公共成员,也包括修改这些属性的状态.例如: public class MyBusinessObject { // Read Only property providing access to a // private data member: private DataSet _ds; public DataSet Data { get