你不知道的东西! c# == 等于运算符 和 Object.Equals()
最近在看 高级点的程序员必看的 CLR via C# 书中说解释了 Object.Equals() 方法的实现, 其中具体的实现用的是 == 运算符 !
以前就对 == 运算符 的具体实现 产生过疑惑 . 它到底对比的什么?
今天刚好手头的东西弄完了,而且还得强制加班中 ! 所以就那今天的加班时间 来认真 来看一下 == 运算符 !
- 最早对于 == 和 Object.Equals() 的了解是来源于 很早以前的一次面试 上面的面试题就有这个问题, 非常遗憾是 当时我水平有限 恰好不知道 怎么回答. 没有回答上来 回家后开始 百度一下答案 看的是迷迷糊 糊的. 有好几种说法. 当时也没太在意 找个了最主流的 就认为是 "真理" 了!
- 阅读 CLR via C# 了解底层实现时 有看到了 Object.Equals() 看到了 内部使用的是 == 运算符 进行的比较 我对以前的标准答案 和 CLR via C# 书中所说的答案 都持有怀疑态度 然后找证据进行研究.我只相信我看到的. 我也庆幸自己 真的看到了.
因为是 Object.Equals() 这方法产生的疑问 所以吧这个内部实行帖出来
public static bool Equals(object objA, object objB)
{
return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
}
// 他首先使用了 == 符号进行对比 然后又 使用对象自己 的 Equals 进行对比
Ps objA.Equals方法得解释一下 . objA使用的Equals方法是 类型自己可能会重写 父类的 Equals 的这个方法.
等于到底做了什么?
关于怎么实现的 非常抱歉的说 我并没有找到 == 运算符的具体实现代码.
但是 可以再MSDN 上看到 它到底干了什么.
http://msdn.microsoft.com/zh-cn/library/53k8ybth.aspx
对于预定义的值类型,如果操作数的值相等,则相等运算符 (==) 返回 true,否则返回 false。 对于 string 以外的引用类型,如果两个操作数引用同一个对象,则 == 返回 true。 对于 string 类型,== 比较字符串的值。
通过MSDN 上的解释 我们可以看到 好像是 它很厉害 能过自动通过类型 进行不同的对比.
其实并未如此
回到我们的更本问题 : == 运算符 对比的到底什么?
如果你了解 栈 和 托管堆 那么你一定知道 数据 字节 一定不是放在 栈 中 就是放在 托管堆 中 所以 == 运算符 对比 不是对比的 栈 中的数据 就是对比的 托管堆中是数据 可以你觉得我说废话了,但是还是要交代清楚:
我们来看一段简单的代码
class Program
{
static void Main(string[] args)
{
Class2 s1 = new Class2();
Class2 s2 = new Class2();
Console.WriteLine(s1 == s2 ? "True" : "false");
}
}
public class Class2
{
}
大家猜一下 是 Ture 还是 False
答案是 False ;
想一下 如果是对比的是 托管堆中的话 那么他们都是创建了 该 对象的 "逐层父类" "类型对象指针" "同步快索引" 等 ;
代码中创建的是一样的 那么 托管堆中的数据就是一样的 如果是对比的 托管堆 那么返回就是 true 显然 程序返回的是 False
而且!
值类型 是存放在 栈 中的 托管堆中并没有东西. 所以 对比 只能是对比 栈中的数据.
说了一堆 就是想先解释清楚 == 运算符对比的是 栈 为什么对比是 栈 而不是托管堆! 我喜欢说的详细点 虽然你可以能觉得啰嗦,我也得巧更多的键盘.但是我认为只有写的很详细了 观看的人才会容易理解.
下面做一些 MSDN 上 == 运算符 对比类型的解释
- 值类型,MSDN上是 预定义的值类型 其实就是他本身只带的值类型 上面的结论是 == 对比是 栈 值类型的对比就没什么说的了
- string 以外的引用类型 他们都有一个特定 那就是他们都是Object 的派生类. 栈中 放入的是 托管堆中的是 地址指向. 那么栈中是 地址指向 由于== 是对比的栈 后面的就不用说多了吧
- string 类型 都说它是个特殊类型 我觉得是以为 微软对其做的 "stirng池" 定义一个 string 变量 并且给 它赋值的时候 会先检查 "stirng池" 中是是否已经有了 同样的的内存数据. 如果True 则 将这个内存地址 的指针 赋值给 栈中的变量名称.
string 类型 重写了 == 运算符 将其使用了 string 类重写的 Equals 方法进行对比;
具体代码段:
public static bool Equals(string a, string b)
{
return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}
public static bool operator ==(string a, string b)
{
return Equals(a, b);
} public static bool operator !=(string a, string b)
{
return !Equals(a, b);
}
关键是 EqualsHelper 这个方法
private static unsafe bool EqualsHelper(string strA, string strB)
{
int length = strA.Length;
if (length != strB.Length)
{
return false;
}
fixed (char* str = ((char*) strA))
{
char* chPtr = str;
fixed (char* str2 = ((char*) strB))
{
char* chPtr2 = str2;
char* chPtr3 = chPtr;
char* chPtr4 = chPtr2;
while (length >= 12)
{
if (((*(((long*) chPtr3)) != *(((long*) chPtr4))) || (*(((long*) (chPtr3 + 4))) != *(((long*) (chPtr4 + 4))))) || (*(((long*) (chPtr3 + 8))) != *(((long*) (chPtr4 + 8)))))
{
break;
}
chPtr3 += 12;
chPtr4 += 12;
length -= 12;
}
while (length > 0)
{
if (*(((int*) chPtr3)) != *(((int*) chPtr4)))
{
break;
}
chPtr3 += 2;
chPtr4 += 2;
length -= 2;
}
return (length <= 0);
}
}
}
它将其每个字符串全部取出单个进行对比. 所有 CLR via C# 中 说是对比的 同等性
现在 == 运算符的 问题都解开了疑惑了
那么让我们重新看一下 Object.Equals 这个方法
public static bool Equals(object objA, object objB)
{
return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
}
这段代码我们前面就看过 是 Object.Equals 是代码, 让我们再次解释一下
- 1 现将比对的对象封装成 跟类型 也就是Object 类 在说一遍封装 又得讲很多 简略一下 就是两个对象 如果是值类型 就将其通过 分装成Object 转换为引用类型 将其通过==运算符 对比栈中的 指向内存的指针.
int s1 = 1;
int s2 = 1;
object o1 = (object) s1;
object o2 = (object) s2;
Console.WriteLine(o1==o2? "true":"fales");这段代码返回的永远都是 Fales
- 2 然后进行 是否非空的验证
- 3 调用 objA 对象的Equals 方法 . 这里是关键 ! 因为这里调用的并不是 Object类的 Equals 方法 而是当前传递过来的对象类 类型的Equals 方法!
几乎所有 类型都会重写 Object 根类的 Equals 即便是当前对象没有重写 那么父类也会重写Object 的 Equals 方法 如 所有值类型对象的根类 System.ValueType 重写了 Equals 方法
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
RuntimeType type = (RuntimeType) base.GetType();
RuntimeType type2 = (RuntimeType) obj.GetType();
if (type2 != type)
{
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]).InternalGetValue(a, false);
object obj4 = ((RtFieldInfo) fields[i]).InternalGetValue(obj, false);
if (obj3 == null)
{
if (obj4 != null)
{
return false;
}
}
else if (!obj3.Equals(obj4))
{
return false;
}
}
return true;
}
这是具体实现代码 看的很明显 是通过反射获取对象的值 进行对比 也就的对比的相等性
但是 ! 我们经常使用的 一些 .net预定义的类 几乎还会接着重写这个方法
如 int32
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public override bool Equals(object obj)
{
return ((obj is int) && (this == ((int) obj)));
} [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public bool Equals(int obj)
{
return (this == obj);
}
代码如上 可以清楚的看到 它并未 获取对比类型Type 搜索所以字段 属性 获取值对比等一系列复杂的 逻辑 就用了 == 运算符进行的对比 为什么 你们自己想去 懒得说了. 系能是一方面 更本原因还是因为预定义的值类型 是栈中存放.
Ps: 写了这么多 观看的你 可以觉得我写的啰啰嗦嗦的, 但我还是认为写的详细点 把 真实的源码贴出来 才能彻底解决所产生的疑惑. 百度上看一眼 人家说的几句话你就能真的明白了嘛?
如有哪里不对 还希望指正!
本人渴望交流 和指点!
你不知道的东西! c# == 等于运算符 和 Object.Equals()的更多相关文章
- 关于 warning CS0659:“***”重写Object.Equals(object o)但不重写Object.GetHashCode()
对象相等性和同一性 System.Object 类型提供了以下方法, namespace System { // // 摘要: // 支持 .NET Framework 类层次结构中的所有类,并为派生 ...
- 【Java编码准则】の #11不要使用Object.equals()来比較密钥值
java.lang.Object.equals()函数默认情况下是不能用来比較组合对象的,比如密钥值.非常多Key类没有覆写equals()函数,因此,组合对象的比較必须单独比較里面的各个类型以保证正 ...
- Java中Object.equals和String.equals的区别详解
前言 Java中的堆和常量池的区别是什么呢?Object.equals与String.equals的区别呢?下面让我们通过一个小示例让你明白它- 1.基础知识 Java的存储空间:寄存器.栈.堆.静态 ...
- object.Equals与object.ReferenceEquals方法
object.Equals方法表达的是语义判等,不一定是引用判等. object.ReferenceEquals方法是肯定是引用判等. 怎么实现一个对象的值语义的 Equals方法?实验. MyCla ...
- 讲的很详细的一篇关于object equals() & hashCode() 的文章
转: 讲的很详细的一篇关于object equals() & hashCode() 的文章 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java ...
- Object equals 方法
package com.mydemo.controller; public class TestEquals { public static void main(String[] args) { Do ...
- java中Object.equals()简单用法
/* equals()方法默认的比较两个对象的引用! */ class Child { int num; public Child(int x){ num = x; } //人文的抛出运行时异常的好处 ...
- Object,equals,toString
一.Object类 说明:Object类是Java中所有的类的直接或者间接的父类(基类). 该类中定义的是所有的类中的都有的的功能. 位置:可以从API中查找. 二.Object类之 equals 方 ...
- 比较运算符compareTo()、equals()、==之间的区别与应用总结
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配 ...
随机推荐
- Python3中的urlencode和urldecode
在Python3中,将中文进行urlencode编码使用函数 urllib.parse.quote(string, safe='/', encoding=None, errors=None) 而将编码 ...
- 出现unmapped spring configuration files found
intell idea启动出现unmapped spring configuration files found提示. 把spring里面的内容都打勾.
- Json与字符串互相转换
jQuery插件支持的转换方式: $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象 浏览器 ...
- python包管理一防丢失
pip3 freeze >list.txt 导出当前环境安装的所有包(list是当前项目录下的文件,可以自己命名)pip3 install -r list.txt 安装文件中所 ...
- python web框架 django 用pycharm 添加django项目
用pycharm 创建django项目 用pycharm 启动django 用项目名启动 点击蓝色连接的url 直接跳转到页面 修改 运行django 程序 设置 可以改端口 可以在创建djang ...
- MySQL对指定字段进行加密(双向加密)
1:建表 test create table test( name varchar(200), value blob ); 插入数据 使用 ENCODE 加密: ,ENCODE('加密字段值', '钥 ...
- 22.解决 eclipse 与 AS 共用 SDK 导致 eclipse ADT 无法使用的问题
相信很多同学在从eclipse 转 AS 都会遇到这个问题,因为方便所以共用了一个sdk 目录,但是AS 会主动更新sdk,然而手贱的跟新了一夜,再打开eclipse的时候瞬间呆滞了,这一夜发生什么了 ...
- Font Awesome-用CSS实现各种小图标icon
Font Awesome为您提供可缩放的矢量图标,您可以使用CSS所提供的所有特性对它们进行更改,包括:大小.颜色.阴影或者其它任何支持的效果.官网:http://fontawesome.dashga ...
- Spring基本功能-扫描与继承
一.Spring的扫描 一个稍大的项目中,可能会有成百上千个bean,此时采用xml的配置形式注入bean,一方面是配置文件显得十分庞大,另一方面也会导致后期的维护难度增加,为 此,Spring引入了 ...
- sidekiq安装及使用
参考:https://github.com/mperham/sidekiq/wiki/Getting-Started https://wdxtub.com/2016/07/06/sidekiq-gui ...