程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#值類型、援用類型中的Equals和==的差別淺析

C#值類型、援用類型中的Equals和==的差別淺析

編輯:C#入門知識

C#值類型、援用類型中的Equals和==的差別淺析。本站提示廣大學習愛好者:(C#值類型、援用類型中的Equals和==的差別淺析)文章只能為提供參考,不一定能成為您想要的結果。以下是C#值類型、援用類型中的Equals和==的差別淺析正文


引言

比來一個同伙正在找任務,他說在口試題中碰到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和==的懂得。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved