首先得出一个结论:==是比较变量内存的数据,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. TinkerPop中的遍历:图的遍历步骤(3/3)

    48 Project Step project() 步骤(map)将当前对象投射到由提供的标签键入的Map<String,Object>中. gremlin> g.V().out(' ...

  3. [raspberry pi3] 串口线使用

    直接上图 GPIO 口说明: 串口线连接: 参考地址: http://bbs.elecfans.com/jishu_596578_1_1.html

  4. 前端文件加载 net::ERR_CONTENT_LENGTH_MISMATCH

    前端文章加载的时候有的时候图片不显示,有的时候文件加载不了,检查nginx设置都没有问题,最近才不显示,经检查是nginx服务器磁盘空间已满,将.log文件移动到其他位置 cp  /dev/null ...

  5. metasploit 读书笔记-EXPLOITATION

    一、渗透攻击基础 1.常用命令 show exploits 显示Meta框架中所有可用的渗透攻击模块。 show options 显示模块所需要的各种参数 back 返回Meta的上一个状态 sear ...

  6. JavaScript 测试和捕捉(try与catch)

    JavaScript 测试和捕捉 try 语句允许我们定义在执行时进行错误测试的代码块. catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块. JavaScript 语句 try ...

  7. Pandas——读取csv,txt文件

    """ 读取csv文件 该文本中的分割符既有空格又有制表符(‘/t’),sep参数用‘/s+’,可以匹配任何空格. """ import p ...

  8. nginx: [emerg] the "ssl" parameter requires ngx_http_ssl_module in /usr/local/nginx//conf/nginx.conf:117

    SSL相关的配置加到了nginx的配置文件中后,nginx竟然启动不起来了 于是用如下命令测试问题所在: /usr/local/nginx/sbin/nginx -c /usr/local/nginx ...

  9. linux内核中的##__VA_ARGS__有什么作用?

    答:   1.__VA_ARGS__是一个可变参数宏: 2. 当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错: 3. 示例 3.1 #defin ...

  10. AngularJs 指令 directive中link,controller 的区别

    其实严格来讲,link和controller是完全不同的概念,这里讲区别有点牵强. angular指令中,带有link和controller两个函数,很多人在写指令的时候不知道是写在link里 还是c ...