2019.03.21 读书笔记 ==与Equals
首先得出一个结论:==是比较变量内存的数据,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的更多相关文章
- 2019.03.21 读书笔记 枚举ENUM
其实没必要为枚举显式赋值,如果赋值了,就一定要全部赋值,否则默认在上一个元素的基础上+1,如果不给枚举变量赋值,就算枚举中没有0元素,也会显示为0,而超出枚举范围的整型数据,也会显示值本身,而不是异常 ...
- 2019.03.21 读书笔记 readonly与const
区别: const是编译时常量(指反编译时看到的源码是常量本身,而不是变量),自带static,只能修饰基元类型.枚举.字符串,readonly是运行时常量(全局变量或者构造赋值),不受类型限制,但在 ...
- 2019.03.21 读书笔记 基元类型的Parse与TryParse 性能与建议
Parse转换失败时会抛出异常,耗损性能,如果转换成功,则与TryParse无差异.查看源码,tryparse的代码更多一些,在失败时,反而性能更优,主要是抛出异常耗损了性能.所以在不确定是用Tryp ...
- 2019.03.29 读书笔记 关于params与可选参数
void Method1(string str, object a){} void Method2(string str, object a,object b) { } void Method3(st ...
- 2019.03.29 读书笔记 关于override与new
差异:override:覆盖父类分方法,new 隐藏父类方法. 共同:都不能改变父类自身方法. public class Test { public string Name { get; set; } ...
- 2019.03.28 读书笔记 关于lock
多线程就离不开lock,lock的本质是一个语法糖,采用了监视器Monitor. lock的参数,错误方式有很多种,只需要记住一种:private static readonly object loc ...
- 2019.03.28 读书笔记 关于try catch
try catch 在不异常的时候不损耗性能,耗损性能的是throw ex,所以在非异常是,不要滥用throw,特别是很多代码习惯:if(age<0) throw new Exception(& ...
- 2019.03.27 读书笔记 关于GC垃圾回收
在介绍GC前,有必要对.net中CLR管理内存区域做简要介绍: 1. 堆栈:用于分配值类型实例.堆栈主要操作系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时,其存储单位自动释放.栈的执行效 ...
- 2019.03.26 读书笔记 关于for与foreach
for 是索引器,foreach是迭代器 foreach在movenext()中增加了对集合版本(一个整数,每次对集合修改都+1)的验证,另外反编译后的效果是使用了using(是try finally ...
随机推荐
- ConcurrentHashMap的putIfAbsent
这个方法在key不存在的时候加入一个值,如果key存在就不放入,等价: if (!map.containsKey(key)) return map.put(key, value); else retu ...
- Nor Flash工作原理
http://blog.chinaunix.net/uid-26876150-id-3723678.html Nor Flash 具有像内存一样的接口,它可以像内存一样读,却不可以像内存一样写,Nor ...
- 非阻塞socket与epoll
阻塞socket. –阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. –对于文件操作read,fread函数调用会将线程阻塞. –对于socket,accept与re ...
- web 后台打印
//提交打印 function sbumitPrint() { printHidden("AppsDSPrintDoub.aspx?type=print"); } function ...
- iOS组件化方案
一.蘑菇街url-block方案 这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中.在使用组件的服务时,通 ...
- MODI中的OCR模块
作者:马健邮箱:stronghorse_mj@hotmail.com发布:2012.07.02更新:2012.07.09补充非简体中文版内容 自从基于MODI的DjVuToy.FreePic2Pdf. ...
- FPM包定制完成 (等待实现 里程碑 1 和 2) 2018年4月13日 2:18:32
前期环境准备: 关闭SELINUX : setenforce 0 关闭SELINUX : sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' / ...
- Ubuntu16.04跑loam_velodyne
Ubuntu16.04,ros kinetic 其实按照github上的README.md来编译就好 cd ~/catkin_ws/src git clone https://github.com/l ...
- 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 ...
- 【bzoj2186】: [Sdoi2008]沙拉公主的困惑 数论-欧拉函数
[bzoj2186]: [Sdoi2008]沙拉公主的困惑 考虑当 gcd(a,b)=1 则 gcd(nb+a,b)=1 所以[1,N!]与M!互质的个数就是 筛出[1,M]所有的素数p[i] 以及逆 ...