首先得出一个结论:==是比较变量内存的数据,Equals是值比较。但是他们都能被重写,所以object又增加了一个RefrenceEquals不可被重写,只比较数据:

  1. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), NonVersionable, __DynamicallyInvokable]
  2. public static bool ReferenceEquals(object objA, object objB) =>
  3. (objA == objB);//判断地址,由于是object,就算是值类型,也会装箱操作,所以变量存的就会是引用地址。

  

然后分类型区别对待:

引用类型:

作为引用类型的基类,在Object中的虚方法Equals,如下:[__DynamicallyInvokable]

  1. public virtual bool Equals(object obj) =>
  2. RuntimeHelpers.Equals(this, obj);//这个方法是引入了外部的函数,具体实现看不到,暂且认定为值相等。比如object a=1;objcet b=1,a.Equals(b)是true就是值相等,
    但是==比较的是变量 a,b存的数据,也就是被装箱后的12的地址。所以a==bfalse
  3. [__DynamicallyInvokable]
  4. public static bool Equals(object objA, object objB) =>
  5. ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));//不可重写的,除了判断==,还判断了Equals,但是Equals被重写后,就以重写的为主了。

值类型:

作为值类型的基类,ValueType,也对Equals做了重写。

  1. [SecuritySafeCritical, __DynamicallyInvokable]
  2. public override bool Equals(object obj)
  3. {
  4. if (obj == null)
  5. {
  6. return false;
  7. }
  8. RuntimeType type = (RuntimeType) base.GetType();
  9. RuntimeType type2 = (RuntimeType) obj.GetType();
  10. if (type2 != type)//比较类型,不然可能值相等,类型不等,比如 int 与 long
  11. {
  12. return false;
  13. }
  14. object a = this;
  15. if (CanCompareBits(this))
  16. {
  17. return FastEqualsCheck(a, obj);
  18. }
  19. FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  20. for (int i = 0; i < fields.Length; i++)
  21. {
  22. object obj3 = ((RtFieldInfo) fields[i]).UnsafeGetValue(a);
  23. object obj4 = ((RtFieldInfo) fields[i]).UnsafeGetValue(obj);
  24. if (obj3 == null)
  25. {
  26. if (obj4 != null)
  27. {
  28. return false;
  29. }
  30. }
  31. else if (!obj3.Equals(obj4))
  32. {
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. //也是引入了一些外部函数,主要作用就是在值相等的基础上,增加了类型比较。至于==,由于变量内存的是数值本身,所以值类型时,==只比较变量的数值。
    当然,尽管在valuetype中重写了Equals,在Int中还是重写了一次,效果也是一样,判断类型,以及类型转换后==比较值
    Int32

[__DynamicallyInvokable]
public override bool Equals(object obj) =>
((obj is uint) && (this == ((uint) obj)));

string类型:

  1. //重写了==
  2. [__DynamicallyInvokable]
  3. public static bool operator ==(string a, string b) =>
  4. Equals(a, b); //从地址对比,改成了调用自己的Equals
  5. //重写的Equals
  6. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), __DynamicallyInvokable]
  7. public override bool Equals(object obj)//重写
  8. {
  9. if (this == null)
  10. {
  11. throw new NullReferenceException();
  12. }
  13. string strB = obj as string;
  14. if (strB == null)
  15. {
  16. return false;
  17. }
  18. if (this == obj)
  19. {
  20. return true;
  21. }
  22. if (this.Length != strB.Length)
  23. {
  24. return false;
  25. }
  26. return EqualsHelper(this, strB);
  27. }
  28. //string类型
  29. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), __DynamicallyInvokable]
  30. public bool Equals(string value)
  31. {
  32. if (this == null)
  33. {
  34. throw new NullReferenceException();
  35. }
  36. if (value == null)
  37. {
  38. return false;
  39. }
  40. if (this == value)
  41. {
  42. return true;
  43. }
  44. if (this.Length != value.Length)
  45. {
  46. return false;
  47. }
  48. return EqualsHelper(this, value);
  49. }
  50. [__DynamicallyInvokable]
  51. public static bool Equals(string a, string b)
  52. {
  53. if (a == b)
  54. {
  55. return true;
  56. }
  57. if ((a == null) || (b == null))
  58. {
  59. return false;
  60. }
  61. if (a.Length != b.Length)
  62. {
  63. return false;
  64. }
  65. return EqualsHelper(a, b);
  66. }
  67. //他们都调用了 EqualsHelper(a, b) 比较值
  68. [SecuritySafeCritical, ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  69. private static unsafe bool EqualsHelper(string strA, string strB)
  70. {
  71. int length = strA.Length;
  72. fixed (char* chRef = &strA.m_firstChar)
  73. {
  74. fixed (char* chRef2 = &strB.m_firstChar)
  75. {
  76. char* chPtr = chRef;
  77. char* chPtr2 = chRef2;
  78. while (length >= 10)
  79. {
  80. if (*(((int*) chPtr)) != *(((int*) chPtr2)))
  81. {
  82. return false;
  83. }
  84. if (*(((int*) (chPtr + 2))) != *(((int*) (chPtr2 + 2))))
  85. {
  86. return false;
  87. }
  88. if (*(((int*) (chPtr + 4))) != *(((int*) (chPtr2 + 4))))
  89. {
  90. return false;
  91. }
  92. if (*(((int*) (chPtr + 6))) != *(((int*) (chPtr2 + 6))))
  93. {
  94. return false;
  95. }
  96. if (*(((int*) (chPtr + 8))) != *(((int*) (chPtr2 + 8))))
  97. {
  98. return false;
  99. }
  100. chPtr += 10;
  101. chPtr2 += 10;
  102. length -= 10;
  103. }
  104. while (length > 0)
  105. {
  106. if (*(((int*) chPtr)) != *(((int*) chPtr2)))
  107. {
  108. break;
  109. }
  110. chPtr += 2;
  111. chPtr2 += 2;
  112. length -= 2;
  113. }
  114. return (length <= 0);
  115. }
  116. }
  117. }//既然有字符串池,为什么还要重写==和Equals呢?
    请看下段官网代码:
    public class Example
    { public static void Main()
    { String s1 = "String1";
    String s2 = "String1";
    Console.WriteLine("s1 = s2: {0}",
    Object.ReferenceEquals(s1, s2));
    Console.WriteLine("{0} interned: {1}", s1, String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes");
    String suffix = "A"; String s3 = "String" + suffix;
    String s4 = "String" + suffix;
    Console.WriteLine("s3 = s4: {0}", Object.ReferenceEquals(s3, s4));
    Console.WriteLine("{0} interned: {1}", s3, String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes"); } }
    // The example displays the following output: // s1 = s2: True // String1 interned: Yes // s3 = s4: False // StringA interned: No
    当两个字符串变量都只想同一个静态的字符串时,refrenceEquals都是true
    当两个变量时运算的结果相等时,refrenceEquals是不相等的。

综上所述:

在值类型中 ==是值比较  Equals是值比较+类型比较

在引用类型中 ==是比较引用,equals是比较值(不过自定义类,都是不相等的,需要自己重写。object装箱操作后,== 是false,Equals是true)

  1. 在自定义类中,都可以对==和Equals重写,不过重写==必须重写!=, 否则报错,重写Equals建议重写gethascode,否则在用到dictionaryhashtable时会出bug,针对于他们都能重写,Object提供了referenceequsls(),作为地址比较。
    结论:
    ==和Equals的区别只有在装箱操作下,才会出现 == false Equals true,自定义类需要重写,建议重写Equals,留==作为引用比较。
    扩展:为什么重写Equals后,要重写GetHashCode()呢?
    因为在DictionaryHashTabble中都是键值对的存储,Key就是他们的HashCode,如果不重写就会导致Equals相等而HashCode不相等,你存入时简单,获取时就找不到了。
  1.  

2019.03.21 读书笔记 ==与Equals的更多相关文章

  1. 2019.03.21 读书笔记 枚举ENUM

    其实没必要为枚举显式赋值,如果赋值了,就一定要全部赋值,否则默认在上一个元素的基础上+1,如果不给枚举变量赋值,就算枚举中没有0元素,也会显示为0,而超出枚举范围的整型数据,也会显示值本身,而不是异常 ...

  2. 2019.03.21 读书笔记 readonly与const

    区别: const是编译时常量(指反编译时看到的源码是常量本身,而不是变量),自带static,只能修饰基元类型.枚举.字符串,readonly是运行时常量(全局变量或者构造赋值),不受类型限制,但在 ...

  3. 2019.03.21 读书笔记 基元类型的Parse与TryParse 性能与建议

    Parse转换失败时会抛出异常,耗损性能,如果转换成功,则与TryParse无差异.查看源码,tryparse的代码更多一些,在失败时,反而性能更优,主要是抛出异常耗损了性能.所以在不确定是用Tryp ...

  4. 2019.03.29 读书笔记 关于params与可选参数

    void Method1(string str, object a){} void Method2(string str, object a,object b) { } void Method3(st ...

  5. 2019.03.29 读书笔记 关于override与new

    差异:override:覆盖父类分方法,new 隐藏父类方法. 共同:都不能改变父类自身方法. public class Test { public string Name { get; set; } ...

  6. 2019.03.28 读书笔记 关于lock

    多线程就离不开lock,lock的本质是一个语法糖,采用了监视器Monitor. lock的参数,错误方式有很多种,只需要记住一种:private static readonly object loc ...

  7. 2019.03.28 读书笔记 关于try catch

    try catch 在不异常的时候不损耗性能,耗损性能的是throw ex,所以在非异常是,不要滥用throw,特别是很多代码习惯:if(age<0) throw new Exception(& ...

  8. 2019.03.27 读书笔记 关于GC垃圾回收

    在介绍GC前,有必要对.net中CLR管理内存区域做简要介绍: 1. 堆栈:用于分配值类型实例.堆栈主要操作系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时,其存储单位自动释放.栈的执行效 ...

  9. 2019.03.26 读书笔记 关于for与foreach

    for 是索引器,foreach是迭代器 foreach在movenext()中增加了对集合版本(一个整数,每次对集合修改都+1)的验证,另外反编译后的效果是使用了using(是try finally ...

随机推荐

  1. ConcurrentHashMap的putIfAbsent

    这个方法在key不存在的时候加入一个值,如果key存在就不放入,等价: if (!map.containsKey(key)) return map.put(key, value); else retu ...

  2. Nor Flash工作原理

    http://blog.chinaunix.net/uid-26876150-id-3723678.html Nor Flash 具有像内存一样的接口,它可以像内存一样读,却不可以像内存一样写,Nor ...

  3. 非阻塞socket与epoll

    阻塞socket. –阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. –对于文件操作read,fread函数调用会将线程阻塞. –对于socket,accept与re ...

  4. web 后台打印

    //提交打印 function sbumitPrint() { printHidden("AppsDSPrintDoub.aspx?type=print"); } function ...

  5. iOS组件化方案

    一.蘑菇街url-block方案 这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中.在使用组件的服务时,通 ...

  6. MODI中的OCR模块

    作者:马健邮箱:stronghorse_mj@hotmail.com发布:2012.07.02更新:2012.07.09补充非简体中文版内容 自从基于MODI的DjVuToy.FreePic2Pdf. ...

  7. FPM包定制完成 (等待实现 里程碑 1 和 2) 2018年4月13日 2:18:32

    前期环境准备: 关闭SELINUX  :   setenforce 0 关闭SELINUX  :   sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' / ...

  8. Ubuntu16.04跑loam_velodyne

    Ubuntu16.04,ros kinetic 其实按照github上的README.md来编译就好 cd ~/catkin_ws/src git clone https://github.com/l ...

  9. L - Large Division (大数, 同余)

    Given two integers, a and b, you should check whether a is divisible by b or not. We know that an in ...

  10. 【bzoj2186】: [Sdoi2008]沙拉公主的困惑 数论-欧拉函数

    [bzoj2186]: [Sdoi2008]沙拉公主的困惑 考虑当 gcd(a,b)=1 则 gcd(nb+a,b)=1 所以[1,N!]与M!互质的个数就是 筛出[1,M]所有的素数p[i] 以及逆 ...