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不相等,你存入时简单,获取时就找不到了。
 

转载于:https://www.cnblogs.com/kafeibuhuizui/p/10573478.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值