Rewrite guidelines for Equals() and operator = = (C# Programming Guide)

Posted by DaiWelsh on Sun, 02 Jan 2022 05:23:24 +0100

1. General

There are two different kinds of equality in C #: reference equality and value equality.

Equal value: it is generally understood that two objects contain the same value. For example, two integers with a value of 2 have value equality.

Equal References: it means that two object references, rather than two objects, are to be compared, and they refer to the same object. This can be achieved by simple assignment, as shown in the following example:

System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true

In the above code, there is only one object, but there are multiple references to that object: a and b.

Because they refer to the same object, they have reference equality. If two objects have reference equality, they also have value equality, but value equality does not guarantee reference equality.

To check reference equality, use ReferenceEquals . To check value equality, use Equals.

2.Equals

This equals generally refers to object Equals(obj)

2.1 Equals in source code

///equals of object
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 Equals method in ValueType

​ The equals method in ValueType overrides the equals method of object.

2.3 about Equals

Since Equals is a virtual method, any class can override its implementation. Any class that represents a value (essentially any value type) or a set of values (such as a complex class) should override Equals. If the type is to implement IComparable , it should override Equals.

The new implementation of Equals should comply with all guarantees of Equals:

  • x.Equals(x) returns true.
  • x.Equals(y) and y.Equals(x) return the same value.
  • If (x.equals (y) & & y.equals (z)) returns true, x.Equals(z) returns true.
  • Subsequent calls to x.Equals(y) return the same value as long as the objects referenced by x and y are not modified.
  • x.Equals (null) returns false (non null value types only). For more information, see Nullable type (C# Programming Guide). )

A new implementation of Equals should not throw an exception. It is recommended that you override any class of Equals as well Object.GetHashCode . In addition to implementing Equals (objects), it is also recommended that all classes implement Equals (types) for their own types to enhance performance. For example:

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. Rewrite operator==

Purpose: compare values by overloading the operator = =.

By default, the operator = = tests whether references are equal by judging whether two references indicate the same object.

Therefore, the reference type does not need to implement the operator = = to obtain this function. When the type is immutable (that is, the data contained in the instance cannot be changed), it may be useful to compare whether the values are equal by overloading the operator = = instead of comparing whether the references are equal, because as an immutable object, it can be regarded as the same as long as its values are the same. It is recommended not to override the operator = = in non immutable types.

Overloaded operator = = implementation should not throw an exception. Any type of overloaded operator = = should also overload operator! =. For example:

//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. Coding in the project

public class UserProperty
{
    int? _requestHashCode;
    /// <summary>
    ///User ID
    /// </summary>
    public int AppUserId { get; set; }
    /// <summary>
    /// key
    /// </summary>
    public string Key { get; set; }
    /// <summary>
    ///Text
    /// </summary>
    public string Text { get; set; }
    /// <summary>
    ///Value
    /// </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);
    }
}

reference material

Rewrite guidelines for Equals() and operator = = (C# Programming Guide)

C # source code