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和==的懂得。