1.概述
C# 中有兩種不同的相等:引用相等和值相等。
值相等:是大家普遍理解的意義上的相等:它意味着兩個對象包含相同的值。例如,兩個值為 2 的整數具有值相等性。
引用相等:意味着要比較的不是兩個對象,而是兩個對象引用,且兩者引用的是同一個對象。這可以通過簡單的賦值來實現,如下面的示例所示:
System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b); //returns true
在上面的代碼中,只存在一個對象,但存在對該對象的多個引用:a 和 b。
由於它們引用的是同一個對象,因此具有引用相等性。如果兩個對象具有引用相等性,則它們也具有值相等性,但是值相等性不能保證引用相等性。
若要檢查引用相等性,應使用 ReferenceEquals。若要檢查值相等性,請使用 Equals。
2.Equals
這個Equals一般指的是Object.Equals(obj)
2.1源代碼中的Equals
///object的equals
public virtual bool Equals(Object obj)
{
return RuntimeHelpers.Equals(this, obj);
}
///RuntimeHelpers.Equals
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public new static extern bool Equals(Object o1, Object o2);
2.2 ValueType中的Equals方法
ValueType中的Equals方法重寫了object的equals方法。
2.3 關於Equals
由於 Equals 是一個虛方法,因此任何類都可以重寫其實現。表示某個值(本質上可以是任何值類型)或一組值(如復數類)的任何類都應該重寫 Equals。如果類型要實現 IComparable,則它應該重寫 Equals。
Equals 的新實現應該遵循 Equals 的所有保證:
- x.Equals(x) 返回 true。
- x.Equals(y) 與 y.Equals(x) 返回相同的值。
- 如果 (x.Equals(y) && y.Equals(z)) 返回 true,則 x.Equals(z) 返回 true。
- 只要不修改 x 和 y 所引用的對象,x.Equals(y) 的后續調用就返回相同的值。
- x.Equals (null) 返回 false(僅非空值類型。有關更多信息,請參見可空類型(C# 編程指南)。)
Equals 的新實現不應該引發異常。建議重寫 Equals 的任何類同時也重寫 Object.GetHashCode。除了實現 Equals(對象)外,還建議所有的類為自己的類型實現 Equals(類型)以增強性能。例如:
class Point
{
internal int x;
internal int y;
public Point(int X, int Y)
{
this.x = X;
this.y = Y;
}
public override bool Equals (Object obj)
{
// Performs an equality check on two points (integer pairs).
if (obj == null || GetType() != obj.GetType()) return false;
Point p = (Point)obj;
return (x == p.x) && (y == p.y);
}
public override int GetHashCode()
{
return Tuple.Create(x, y).GetHashCode();
}
}
3. 重寫運算符 ==
目的:通過重載運算符 == 來比較值是否相等。
默認情況下,運算符 == 通過判斷兩個引用是否指示同一對象來測試引用是否相等。
因此引用類型不需要實現運算符 == 就能獲得此功能。當類型不可變(即實例中包含的數據不可更改)時,通過重載運算符 == 來比較值是否相等而不是比較引用是否相等可能會很有用,因為作為不可變的對象,只要其值相同,就可以將其視為相同。建議不要在非不可變類型中重寫運算符 ==。
重載的運算符 == 實現不應引發異常。重載運算符 == 的任何類型還應重載運算符 !=。例如:
//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.x == b.x && a.y == b.y && a.z == b.z;
}
public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
return !(a == b);
}
4.項目中代碼編寫
public class UserProperty
{
int? _requestHashCode;
/// <summary>
/// 用戶ID
/// </summary>
public int AppUserId { get; set; }
/// <summary>
/// key
/// </summary>
public string Key { get; set; }
/// <summary>
/// 文本
/// </summary>
public string Text { get; set; }
/// <summary>
/// 值
/// </summary>
public string Value { get; set; }
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestHashCode.HasValue)
_requestHashCode = (this.Key + this.Value).GetHashCode();
return _requestHashCode.Value;
}
return base.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is UserProperty))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
UserProperty item = (UserProperty)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Key == this.Key && item.Value == this.Value;
}
public bool IsTransient()
{
return string.IsNullOrEmpty(this.Key) || string.IsNullOrEmpty(this.Value);
}
public static bool operator ==(UserProperty left, UserProperty right)
{
if (Object.Equals(left, null))
{
return (Object.Equals(right, null)) ? true : false;
}
else
{
return left.Equals(right);
}
}
public static bool operator !=(UserProperty left, UserProperty right)
{
return !(left == right);
}
}