很多C#的教材都会强调对象相等的概念。我们都知道,在C#的世界里存在两种等同性。一种是逻辑等同性:如果两个对象在逻辑上代表同样的值,则称他们具有逻辑等同性。另一种是引用等同性:如果两个引用指向同一个对象实例,则称他们具有引用等同性。

  众所周知,Object类型有一个名为Equals的实例方法可以用来确定两个对象是否相等。Object的Equals的默认实现比较的是两个对象的引用等同性。而Object的派生类ValueTpye重写了Equals方法,它比较的是两个对象的逻辑等同性。也就是说,在C#里,引用类型的默认Equals版本关注的是引用等同性,而值类型关注的是逻辑等同性。当然,这并不总能满足我们的要求。所以每当我们更在意引用类型的逻辑等同性的时候,我们就应该重写Equals方法。

  重写引用类型的Equals方法以改变其默认的比较方式的一个著名例子是String类。当我们写出string1.Equals(string2)这样的代码时,我们比较的不是string1和string2这两个引用所指向的是否为同一个实例(引用等同性),而是比较string1与string2所包含的字符序列是否相同(逻辑等同性)。

  误解一:Equals方法和operator==具有相同的默认行为。

  对于引用类型,如果没有为它重载==操作符,且其父类型也没有重写Equals方法,则这个引用类型Equals方法和operator==具有相同的默认行为,即它们比较的都是对象的引用等同性。然而对于值类型来说,就完全不是这么回事了!因为如果你没有为自定义值类型重载operator==的话,就不能写这样的代码myStruct1 == myStruct2,否则会得到一个编译错误,原因是值类型没有相等操作符重载的默认实现。

  误解二:自定义类的Equals的方法默认实现将自动调用operator==方法,或operator==方法的默认实现将自动调用Equals方法。

  经常听到有人说某某类型是引用类型,所以它的Equals方法的默认实现将自动调用operator==方法。这种说法完全是没有道理的。正如上文所说的,引用类型Equals方法的默认实现来自Object,而值类型的默认实现来自TypeValue,就算他们会使用==操作符,使用的也是Object或TypeValue的重载版本。原则上来说,只要我们没有重写一个类的Equals方法,那么它就会继承其父类的实现,而父类是没有机会使用子类型的操作符重载的。同样,只要我们没有在一个类的==操作符重载中调用Equals方法,它是不会自动调用的。

  误解三:值类型的默认Equals实现是对两个对象进行逐位比较的。

  有些人认为值类型的Equals默认实现就是通过比较两个对象在内存中的位表示,即如果所有的二进制位都相等,则说明这两个对象等同。这是不准确的。因为其实值类型的Equals默认实现是对值类型的每个字段都调用该字段类型的Equals方法,如果所有字段的Equals方法都返回true,则他们才可能相等。来看一个例子:

class MyClass
{
     public override bool Equals(object obj)
    {
        Console.WriteLine("MyClass的Equals方法被调用了。");
        return true;
    }
} struct MyStruct
{
     public MyClass Filed;
} class Program
{
     staticvoid Main(string[] args)
     {
         MyStruct a;
         MyStruct b;
         a.Filed = new MyClass();
         b.Filed = new MyClass();
         Console.WriteLine(a.Equals(b));
      }

  很显然,a和b拥有完全不同的二进制位表示。但是最终打印的结果是:

MyClass的Equals方法被调用了。
True

  这说明值类型的默认实现是通过调用字段的Equals方法来确定两个对象是否相等,而不是通过比较他们的二进制位是否一致来确定的。

  误解四:Equals是非常基本、非常常用的方法,所以其默认的实现不存在性能问题。

  对于引用类型,Equals的默认实现很简单,仅仅需要判断两个引用是不是同一种类型、两个引用指向的是不是同一块内存就可以了。所以其性能也没有问题。但是对于值类型,Equals的任务就没有这么简单了。它需要对两个对象的所有字段都做出比较,即逐字段调用字段类型的Equals。由于在ValueType(值类型Equals方法默认实现的位置)中,不可能知道它所有的子类型都包含哪些字段,所以为了调用子类型字段的Equals方法,ValueType的Equals就需要使用反射技术。您可能已经看出来了,反射并不是一种性能友好的技术,所以值类型的Equals方法算不上高效。这也正是为什么微软推荐我们为自定义值类型重写Equals方法的原因。


自己的理解:

1、object 自带的Equals方法,是引用比较

 

c# Equals方法的更多相关文章

  1. List<T>Find方法,FindAll方法,Contains方法,Equals方法

    假如传入的T是一个类, List<MessageInfos> MessageInfos = new List<MessageInfos>(); MessageInfos= Me ...

  2. [java] 更好的书写equals方法-汇率换算器的实现(4)

    [java] 更好的书写equals方法-汇率换算器的实现(4) // */ // ]]>   [java] 更好的书写equals方法-汇率换算器的实现(4) Table of Content ...

  3. java中的equals()方法重写

    如何java中默认的equals方法跟实际不符的话,需要重写equals方法.例如: public class TestEquals { public static void main(String[ ...

  4. java提高篇(十三)-----equals()方法总结

    equals() 超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等.该方法的源码如下: public boolean equals(Object obj) { retu ...

  5. Java中“==”与equals方法的区别

    1. 用“==”比较两个变量,如果两个变量是基本类型变量,且都是数值类,则值相等就返回true 如果两个变量是引用型变量,则两个对象的地址一样,即指向同一个对象,则返回true 2.equals:St ...

  6. equals()方法

    equals()方法是根类Object中的一个方法,子类可以根据需要重写该方法(比如:String类). 一.Object类中的equals()方法实现如下: public boolean equal ...

  7. C#中==与Equals方法的区别

    using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace Console ...

  8. java重写equals方法

    @Override public int hashCode() { return task.getId(); } @Override public boolean equals(Object obj) ...

  9. 重写Object类中的equals方法

    Object是所有类的父亲,这个类有很多方法,我们都可以直接调用,但有些方法并不适合,例如下面的student类 public class Student { //姓名.学号.年纪 private S ...

  10. java中hashCode方法与equals方法的用法总结

    首先,想要明白hashCode的作用,必须要先知道Java中的集合. 总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重 ...

随机推荐

  1. 20169202 2016-2017-2《Windows攻击》

    Windows攻击 实验要求:使用Metaspoit攻击MS08-067,提交正确得到远程Shell的截图,加上自己的学号水印 (1):MS08-067远程溢出漏洞描述 MS08-067漏洞的全称为& ...

  2. Appium之手机屏幕亮度控制条处理

    手机设置下的屏幕亮度控制条看上去是悬浮的,想手动调整亮度有两种方法:一.在控制条上左右任意拖动:二.在控制条上点击任意一点.如下图:

  3. javascript与java的相互调用,纯java的javascript引擎rhino(转载)

    1.下载Rhino安装包,下载地址:官网http://www.mozilla.org/rhino. 2.rhino环境配置,把解压出来的js.jar文件加入到系统的环境变量classpath 3.在命 ...

  4. MVC控制器中动作方法返回的结果

    在MVC控制器中主要的返回方式有如下几种: 1.Content(): 返回文本类型的ContentResult,比如“这是我做的一个MVC”. 2.File(): 返回文件类型的内容FileResul ...

  5. C# 4种方法计算斐波那契数列 Fibonacci

    F1: 迭代法 最慢,复杂度最高 F2: 直接法 F3: 矩阵法 参考<算法之道(The Way of Algorithm)>第38页-魔鬼序列:斐波那契序列 F4: 通项公式法 由于公式 ...

  6. C# AutoMapper的简单扩展

    AutoMapper可以很方便的将一个实体的属性值转化给另一个对象.这个功能在我们日常的编码中经常会遇到.我将AutoMapper的一些基本映射功能做成扩展方法,在编码中更方便使用. using Sy ...

  7. update sharepoint 2013 cu error

    1. 安装过程合理: A. 可以同时在管理中心.两台前端.搜索服务器上安装重新发布的SP1补丁包(所提供的链接) B. 等待所有SP1补丁包安装完成,依次在管理中心.两台前端.搜索服务器上运行配置向导 ...

  8. Android 屏幕适应

    基础知识: 屏幕密度: Density-independent pixel (dp):密度无关像素单位(一个相对的值).1dp 的大小相当于一个 160 dpi 屏幕上一个像素的大小. 计算方法:px ...

  9. 激活IDEA,pycharm方法

    1.修改hosts文件将0.0.0.0 account.jetbrains.com添加到hosts文件最后,注意hosts文件无后缀,如果遇到无法修改或权限问题,可以采用覆盖的方法去替换hosts文件 ...

  10. vsftpd服务器配置虚拟用户

    添加宿主用户 新建系统用户vsftpd,用户目录为/home/wwwroot, 用户登录终端设为/bin/false(即使之不能登录系统) useradd vsftpd -d /home/wwwroo ...