亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#值類型、引用類型中的Equals和==的區別淺析

2020-01-24 02:12:22
字體:
來源:轉載
供稿:網友

引言

最近一個朋友正在找工作,他說在筆試題中遇到Equals和==有什么區別的題,當時跟他說如果是值類型的,它們沒有區別,如果是引用類型的有區別,但string類型除外。為了證實自己的說法,也研究了一下,以免誤導別人,這里將研究結果總結一下,如果我有什么地方說的不對的地方,望指出。

相等性

在定義類或結構時,您將決定為類型創建值相等性(或等效性)的自定義定義是否有意義。 通常,當類型的對象預期要添加到某類集合時,或者當這些對象主要用于存儲一組字段或屬性時,您將實現值相等性。 您可以基于類型中所有字段和屬性的比較來定義值相等性,也可以基于子集進行定義。 但在任何一種情況下,類和結構中的實現均應遵循五個等效性保證條件:

1.x.Equals(x) 返回 true. 。這稱為自反屬性。
2.x.Equals(y) 返回與 Equals(x) 相同的值。 這稱為對稱屬性。
3.如果 (x.Equals(y) && y.Equals(z)) 返回 true,則 x.Equals(z) 返回 true。 這稱為可傳遞屬性。
4.只要不修改 x 和 y 所引用的對象,x.Equals(y) 的后續調用就返回相同的值。
5.x.Equals(null) 返回 false。 但是,null.Equals(null) 會引發異常;它不遵循上面的第二條規則。

您定義的任何結構已經具有它從 Object.Equals(Object) 方法的 System.ValueType 重寫中繼承的默認值相等性實現。 此實現使用反射來檢查類型中的所有公共和非公共字段以及屬性。 盡管此實現可生成正確的結果,但與您專門為類型編寫的自定義實現相比,它的速度相對較慢。

類和結構的值相等性的實現詳細信息不同。 但是,類和結構都需要相同的基礎步驟來實現相等性:

重寫 Object.Equals(Object)虛方法。 大多數情況下,您的 bool Equals( object obj ) 實現應只調入作為 System.IEquatable<T> 接口的實現的類型特定 Equals 方法。 (請參見步驟 2。)

通過提供類型特定的 Equals 方法實現 System.IEquatable<T> 接口。 實際的等效性比較將在此接口中執行。 例如,您可能決定通過僅比較類型中的一兩個字段來定義相等性。 不要從 Equals 中引發異常。 僅適用于類:此方法應僅檢查類中聲明的字段。 它應調用 base.Equals 來檢查基類中的字段。 (如果類型直接從 Object 中繼承,則不要這樣做,因為 Object.Equals(Object) 的 Object 實現會執行引用相等性檢查。)

可選,但建議這樣做:重載 == 和 != 運算符。

重寫 Object.GetHashCode,使具有值相等性的兩個對象生成相同的哈希代碼。

可選:若要支持“大于”或“小于”定義,請為類型實現 IComparable<T> 接口,并同時重載 <= 和 >= 運算符。

                         ――MSDN(http://msdn.microsoft.com/zh-cn/library/dd183755.aspx)這里將msdn的說法貼在此處,方便查看。

值類型

這里就以int類型的為代表進行分析,在分析之前先復習一下什么是重載?重載:簡單的說就是一個類中的方法與另一個方法同名,但是參數列表個數或者類型或者返回值類型不同,則這兩個方法構成重載。那么重載方法的調用規則是什么?那么先看下面的一段測試代碼:

復制代碼 代碼如下:

namespace Wolfy.EqualsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int a =1, b = 1;
            Console.WriteLine(Add(a,b));
            Console.Read();
        }
        static int Add(object a, object b)
        {
            Console.WriteLine("調用了object類型參數列表的方法:");
            return (int)a + (int)b;
        }
        static int Add(int a, float b)
        {
            Console.WriteLine("調用了int,float類型參數列表的方法:");
            return a + (int)b;
        }
        static int Add(int a, int b)
        {
            Console.WriteLine("調用了int類型參數列表的方法:");
            return a + b;
        }
    }
}

測試結果:

說明根據傳入實參的類型,優先匹配最相近的形參列表的方法。

那么我們將Add(int a ,int b)這個方法注釋掉,那么會調用哪個方法?

為什么花費那么多口舌說明上面的問題,那么現在看一下Int32反編譯的代碼:

Int32有一個自己的Equals方法,有一個重寫的Equals方法,如果兩個int類型的值進行比較,Equals和==是一樣的,因為它優先調用了下面的Equals方法,如果是下面的代碼,則會選擇重寫的Equals方法。

復制代碼 代碼如下:

static void Main(string[] args)
        {
            int a = 1;
            object b = 1;
            Console.WriteLine(a.Equals(b));
            Console.Read();
        }

可見,對于值類型的Equals和==是一樣的。

引用類型

在類(引用類型)上,兩種 Object.Equals(Object) 方法的默認實現均執行引用相等性比較,而不是值相等性檢查。 當實施者重寫虛方法時,目的是為了為其指定值相等性語義。
即使類不重載 == 和 != 運算符,也可以將這些運算符與類一起使用。 但是,默認行為是執行引用相等性檢查。 在類中,如果您重載 Equals 方法,則應重載 == 和 != 運算符,但這并不是必需的。

                                                                ――MSDN

測試代碼:

復制代碼 代碼如下:

static void Main(string[] args)
        {

            Person p1 = new Person() { Name = "wolfy" };
            Person p2 = new Person() { Name = "wolfy" };
            Person p3 = p2;
            bool r1 = p1 == p2;
            bool r2 = p1.Equals(p2);
            bool r3 = p2 == p3;
            bool r4 = p2.Equals(p3);
            bool r5 = object.ReferenceEquals(p1, p2);
            bool r6 = object.Equals(p1,p2);
            bool r7 = object.ReferenceEquals(p2, p3);
            Console.WriteLine("==/t"+r1);
            Console.WriteLine("Equals/t"+r2);
            Console.WriteLine("p3=p2/t"+r3);
            Console.WriteLine("p2.Equals(p3)/t"+r4);
            Console.WriteLine("object.ReferenceEquals/t" + r5);
            Console.WriteLine("object.Equals(p1,p2)/t" + r6);
            Console.WriteLine("object.ReferenceEquals(p2, p3)/t" + r7);
            Console.Read();
        }

結果:

順便反編譯一下Equals和ReferenceEquals方法,看看他們的實現如何?

復制代碼 代碼如下:

 // object
 [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
 public static bool Equals(object objA, object objB)
 {
     return objA == objB || (objA != null && objB != null && objA.Equals(objB));
 }

// object
[__DynamicallyInvokable, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public static bool ReferenceEquals(object objA, object objB)
{
    return objA == objB;
}

通過上面的代碼,我們可以得出這樣的結論,引用類型中Equals和ReferenceEquals的行為是相同的,==與ReferenceEquals的行為也相同,但string除外。

對特殊應用類型string的相等性,遵循值類型的相等性。string類型的反編譯后的Equals方法和==代碼如下:

復制代碼 代碼如下:

public override bool Equals(object obj)
        {
            if (this == null)
            {
                throw new NullReferenceException();
            }
            string text = obj as string;
            return text != null && (object.ReferenceEquals(this, obj) || (this.Length == text.Length && string.EqualsHelper(this, text)));
        }
        [__DynamicallyInvokable, ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public bool Equals(string value)
        {
            if (this == null)
            {
                throw new NullReferenceException();
            }
            return value != null && (object.ReferenceEquals(this, value) || (this.Length == value.Length && string.EqualsHelper(this, value)));
        }
        [__DynamicallyInvokable, SecuritySafeCritical]
        public bool Equals(string value, StringComparison comparisonType)
        {
            if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
            {
                throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
            }
            if (this == value)
            {
                return true;
            }
            if (value == null)
            {
                return false;
            }
            switch (comparisonType)
            {
            case StringComparison.CurrentCulture:
                return CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0;
            case StringComparison.CurrentCultureIgnoreCase:
                return CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0;
            case StringComparison.InvariantCulture:
                return CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0;
            case StringComparison.InvariantCultureIgnoreCase:
                return CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0;
            case StringComparison.Ordinal:
                return this.Length == value.Length && string.EqualsHelper(this, value);
            case StringComparison.OrdinalIgnoreCase:
                if (this.Length != value.Length)
                {
                    return false;
                }
                if (this.IsAscii() && value.IsAscii())
                {
                    return string.CompareOrdinalIgnoreCaseHelper(this, value) == 0;
                }
                return TextInfo.CompareOrdinalIgnoreCase(this, value) == 0;
            default:
                throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
            }
        }
        [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public static bool Equals(string a, string b)
        {
            return a == b || (a != null && b != null && a.Length == b.Length && string.EqualsHelper(a, b));
        }
        [__DynamicallyInvokable, SecuritySafeCritical]
        public static bool Equals(string a, string b, StringComparison comparisonType)
        {
            if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
            {
                throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
            }
            if (a == b)
            {
                return true;
            }
            if (a == null || b == null)
            {
                return false;
            }
            switch (comparisonType)
            {
            case StringComparison.CurrentCulture:
                return CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0;
            case StringComparison.CurrentCultureIgnoreCase:
                return CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0;
            case StringComparison.InvariantCulture:
                return CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0;
            case StringComparison.InvariantCultureIgnoreCase:
                return CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0;
            case StringComparison.Ordinal:
                return a.Length == b.Length && string.EqualsHelper(a, b);
            case StringComparison.OrdinalIgnoreCase:
                if (a.Length != b.Length)
                {
                    return false;
                }
                if (a.IsAscii() && b.IsAscii())
                {
                    return string.CompareOrdinalIgnoreCaseHelper(a, b) == 0;
                }
                return TextInfo.CompareOrdinalIgnoreCase(a, b) == 0;
            default:
                throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
            }
        }
        [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public static bool operator ==(string a, string b)
        {
            return string.Equals(a, b);
        }
        [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public static bool operator !=(string a, string b)
        {
            return !string.Equals(a, b);
        }

從上面的代碼可以看出string類型的Equals和==是一樣的。

復制代碼 代碼如下:

public static bool operator ==(string a, string b)
        {
            return string.Equals(a, b);
        }
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public static bool operator !=(string a, string b)
        {
            return !string.Equals(a, b);
        }

總結

值類型具有它從 Object.Equals(Object) 方法的 System.ValueType 重寫中繼承的默認值相等性實現。特殊的引用類型string類型,因為重寫了Equals和==方法,所以string類型的Equals和==與值類型的相等性一樣。對于其他的引用類型此時的Equals和==與引用相等ReferenceEquals的行為相同。

以上是由一個同事的問題引起,中間也查了很多資料,發現這篇文章在草稿箱中躺了很久了,今天突然看到就拿出來曬曬。中間修修改改,總嘗試著用哪種方式來說明這個老生常談的問題更好些。以上有些觀點,純屬個人見解,如果你有更好的理解方式,不妨分享一下。如果對你有所幫助,不妨點一下推薦,讓更多的人看到,說說自己對Equals和==的理解。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲激情视频在线观看| 中文字幕亚洲情99在线| 日韩视频在线免费| 91av在线播放| 亚洲国产欧美在线成人app| 欧美劲爆第一页| 国产精品一区二区久久久| 色无极影院亚洲| 国产日韩中文字幕| 亚洲免费视频一区二区| 久久亚洲精品小早川怜子66| 日韩一区二区三区在线播放| 亚洲日韩第一页| 69久久夜色精品国产69| 国产精品久久77777| 亚洲欧美精品在线| 亚洲成人精品久久久| 日韩免费视频在线观看| 国产精品福利在线观看| 日韩hd视频在线观看| 欧美激情三级免费| 欧美国产日韩一区二区在线观看| 2018中文字幕一区二区三区| 成人激情免费在线| 韩剧1988免费观看全集| 亚洲精品一区中文字幕乱码| 欧美日韩国产一区二区三区| 亚洲精品大尺度| 国产精品高潮粉嫩av| 日韩精品视频免费专区在线播放| 欧美日韩在线视频观看| 亚洲视频电影图片偷拍一区| 久久久久久久久久久人体| 国产成人拍精品视频午夜网站| 中文字幕精品在线视频| 国产精品日韩欧美综合| 日韩欧美在线播放| 欧美日韩激情小视频| 国产精品成人在线| 久久精品国产欧美亚洲人人爽| 色www亚洲国产张柏芝| 黑人巨大精品欧美一区免费视频| 国产精品成人久久久久| 欧美乱大交xxxxx| 亚洲最大成人网色| 久久久爽爽爽美女图片| 狠狠久久亚洲欧美专区| 日韩视频一区在线| y97精品国产97久久久久久| 日韩欧美一区二区三区| 欧美香蕉大胸在线视频观看| 亚洲精品福利资源站| 91久久久在线| 成人激情在线播放| 国产精品伦子伦免费视频| 日韩大片免费观看视频播放| 免费97视频在线精品国自产拍| 91在线看www| 午夜精品福利在线观看| 精品亚洲国产视频| 日韩国产高清污视频在线观看| 亚洲一区二区三区乱码aⅴ蜜桃女| 在线视频国产日韩| 人人澡人人澡人人看欧美| 精品久久久久久久久久久| 78m国产成人精品视频| 欧美日韩在线另类| 国产不卡精品视男人的天堂| 亚洲精品电影在线观看| 亚洲精品视频在线观看视频| 欧美最近摘花xxxx摘花| 亚洲天堂一区二区三区| 95av在线视频| 国产成人精品日本亚洲专区61| 亚洲一区二区福利| 国产精品美女久久久久久免费| 92版电视剧仙鹤神针在线观看| 日韩欧美亚洲综合| 亚洲国产精品久久久| 色偷偷91综合久久噜噜| 日韩在线观看免费全集电视剧网站| 国产一区二区三区丝袜| 国产精品久久不能| 中文字幕在线视频日韩| 亚洲日本中文字幕免费在线不卡| 国产视频久久久久| 欧美激情亚洲精品| 亚洲福利视频网| 亚洲已满18点击进入在线看片| 久久久国产精品亚洲一区| 91精品国产91久久| 久久久久久国产三级电影| 日韩一区二区三区国产| 亚洲精品一区在线观看香蕉| 亚洲综合中文字幕在线观看| 亚洲人在线视频| 国产精品日韩久久久久| 成人美女免费网站视频| 日韩欧美精品在线观看| 精品高清一区二区三区| 国产精品日韩久久久久| 中文字幕av一区二区| 91香蕉嫩草神马影院在线观看| 亚洲一区二区免费在线| 91av在线国产| 欧美日韩一区二区免费视频| 久久久免费在线观看| 国产一区视频在线播放| 国产精品扒开腿做爽爽爽视频| 一本一本久久a久久精品综合小说| 亚洲色图欧美制服丝袜另类第一页| 欧美日韩在线免费观看| 成人激情春色网| 国产综合香蕉五月婷在线| 最近2019好看的中文字幕免费| 欧美日韩国产一区在线| 欧美大奶子在线| 欧洲成人在线观看| 国产日产欧美精品| 国内精品视频在线| 日本道色综合久久影院| 日韩在线视频中文字幕| 日韩精品中文字幕久久臀| 亚洲精品自拍第一页| 欧美影院成年免费版| 5278欧美一区二区三区| 国产香蕉一区二区三区在线视频| 日本19禁啪啪免费观看www| 欧美肥老妇视频| 亚洲一区二区中文| 51色欧美片视频在线观看| 国产日韩欧美日韩大片| 精品久久久久久亚洲国产300| 日韩成人激情影院| 欧美性jizz18性欧美| 最好看的2019的中文字幕视频| 91国在线精品国内播放| 亚洲最新在线视频| 亚洲美女精品久久| 欧美极品少妇xxxxⅹ免费视频| 成人免费淫片aa视频免费| 久久久亚洲国产| 日韩在线视频一区| 成人免费在线网址| 国产精品91在线| 欧美中文字幕第一页| 精品国产一区二区三区四区在线观看| 欧美激情精品久久久久久变态| 91精品国产精品| 蜜臀久久99精品久久久久久宅男| 亚洲伊人一本大道中文字幕| 亚洲奶大毛多的老太婆| 亚洲男人的天堂在线播放| 青青久久av北条麻妃海外网| 亚洲第一福利网站| 亚洲欧美视频在线| 中文字幕在线亚洲| 91精品国产高清久久久久久久久| 国产欧美日韩视频| 欧美重口另类videos人妖| 国产精品一区二区久久久久| 日本乱人伦a精品| 国产在线视频不卡| 久久久国产视频|