1. 程式人生 > 其它 >Equals() 和運算子 == 的重寫準則(C# 程式設計指南)

Equals() 和運算子 == 的重寫準則(C# 程式設計指南)

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);
    }
}

參考資料

Equals() 和運算子 == 的重寫準則(C# 程式設計指南)

C#原始碼