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

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

  

然后分类型区别对待:

引用类型:

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

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

值类型:

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

[SecuritySafeCritical, __DynamicallyInvokable]
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
RuntimeType type = (RuntimeType) base.GetType();
RuntimeType type2 = (RuntimeType) obj.GetType();
if (type2 != type)//比较类型,不然可能值相等,类型不等,比如 int 与 long
{
return false;
}
object a = this;
if (CanCompareBits(this))
{
return FastEqualsCheck(a, obj);
}
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++)
{
object obj3 = ((RtFieldInfo) fields[i]).UnsafeGetValue(a);
object obj4 = ((RtFieldInfo) fields[i]).UnsafeGetValue(obj);
if (obj3 == null)
{
if (obj4 != null)
{
return false;
}
}
else if (!obj3.Equals(obj4))
{
return false;
}
}
return true;
}
//也是引入了一些外部函数,主要作用就是在值相等的基础上,增加了类型比较。至于==,由于变量内存的是数值本身,所以值类型时,==只比较变量的数值。
当然,尽管在valuetype中重写了Equals,在Int中还是重写了一次,效果也是一样,判断类型,以及类型转换后==比较值
Int32:

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

string类型:

//重写了==
[__DynamicallyInvokable]
public static bool operator ==(string a, string b) =>
Equals(a, b); //从地址对比,改成了调用自己的Equals
//重写的Equals
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), __DynamicallyInvokable]
public override bool Equals(object obj)//重写
{
if (this == null)
{
throw new NullReferenceException();
}
string strB = obj as string;
if (strB == null)
{
return false;
}
if (this == obj)
{
return true;
}
if (this.Length != strB.Length)
{
return false;
}
return EqualsHelper(this, strB);
}
//string类型
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), __DynamicallyInvokable]
public bool Equals(string value)
{
if (this == null)
{
throw new NullReferenceException();
}
if (value == null)
{
return false;
}
if (this == value)
{
return true;
}
if (this.Length != value.Length)
{
return false;
}
return EqualsHelper(this, value);
}
[__DynamicallyInvokable]
public static bool Equals(string a, string b)
{
if (a == b)
{
return true;
}
if ((a == null) || (b == null))
{
return false;
}
if (a.Length != b.Length)
{
return false;
}
return EqualsHelper(a, b);
}
//他们都调用了 EqualsHelper(a, b) 比较值
[SecuritySafeCritical, ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private static unsafe bool EqualsHelper(string strA, string strB)
{
int length = strA.Length;
fixed (char* chRef = &strA.m_firstChar)
{
fixed (char* chRef2 = &strB.m_firstChar)
{
char* chPtr = chRef;
char* chPtr2 = chRef2;
while (length >= 10)
{
if (*(((int*) chPtr)) != *(((int*) chPtr2)))
{
return false;
}
if (*(((int*) (chPtr + 2))) != *(((int*) (chPtr2 + 2))))
{
return false;
}
if (*(((int*) (chPtr + 4))) != *(((int*) (chPtr2 + 4))))
{
return false;
}
if (*(((int*) (chPtr + 6))) != *(((int*) (chPtr2 + 6))))
{
return false;
}
if (*(((int*) (chPtr + 8))) != *(((int*) (chPtr2 + 8))))
{
return false;
}
chPtr += 10;
chPtr2 += 10;
length -= 10;
}
while (length > 0)
{
if (*(((int*) chPtr)) != *(((int*) chPtr2)))
{
break;
}
chPtr += 2;
chPtr2 += 2;
length -= 2;
}
return (length <= 0);
}
}
}//既然有字符串池,为什么还要重写==和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)

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

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. 5.内网渗透之PTH&PTT&PTK

    ---------------------------------------------- 本文参考自三好学生-域渗透系列文章 内网渗透之PTH&PTT&PTK PTH(pass-t ...

  2. php 函数追踪扩展 phptrace

    php 函数追踪扩展 phptrace 介绍 phptrace 是一个低开销的用于跟踪.分析 php 运行情况的工具. 它可以跟踪 php 在运行时的函数调用.请求信息.执行流程.并且提供有过滤器.统 ...

  3. [转载]Redhat Enterprise 6.1 如何使用免费的CentOS的yum源

    Redhat Enterprise 6.1 如何使用免费的CentOS的yum源 graybull posted @ 2013年2月18日 22:29 in Unix/Linux with tags  ...

  4. 【转】基于OCS实现高速缓存

    OCS简介 OCS( Open Cache Service)为分布式高速缓存服务,主要实现热点数据的快速响应: OCS支持Key/Value的数据结构,兼容memcachebinary protoco ...

  5. HBase - 伪分布式安装过程

    环境 - hadoop - 没有zookeeper(用hbase自带的zookeeper,当然后期我会改用独立的zookeeper) HBase介绍 参考:hbase是什么? hbase下载 地址:h ...

  6. 内容可循环重用的ScrollView

    UIScrollView是iOS中最常用的交互控件之一,本文讨论当设定为翻页模式,内容页很多的时候,如果给每个页面都创建一个新View,会导致资源爆表.比较好的做法是参考UITableViewCell ...

  7. NSNull空值

    1.前言 作为占据空间的一个空值,如用在数组或字典中占据一个没有任何值的空间. 1.1 NULL & nil 的区别: nil 是 OC 的,空对象,地址指向空的对象,指针地址指向的是 NUL ...

  8. iframe相关操作

    内容摘要 父级获取iframe中的元素 父级触发iframe中的函数 iframe触发父级元素绑定的事件 iframe触发父级函数方法 iframe触发父级元素的值 parent.html <! ...

  9. numpy中argsort函数用法

    在Python中使用help帮助 >>> import numpy >>> help(numpy.argsort) Help on function argsort ...

  10. winform datagridview不显示滚动条

    datagridview 数据行数已经超出表格显示范围了,为什么右侧没有滚动条呢? 这个其实不是DataGridView的问题,实际上滚动条是出现了的,但被其他东西挡住了.如果网格是放在panel上的 ...