August 30, 2005

Equals Operator Pattern

Equality in C#

Pattern for implementing required operators to support '==', '!=', 'Equals()' and 'GetHashCode()' for a new reference or value type. Need to implement these methods together.

For REFERENCE types
public override bool Equals(object rhs)
{
    if (rhs == null) // this cannot be null
        return false;

    if (object.ReferenceEquals(this, rhs)
        return true;

    // Check for null values and compare run-time types.
    if (this.GetType() != rhs.GetType())
         return false;

    return CompareFields(rhs as XXX);
}

private bool CompareFields(XXX p)
{ // Field by field comparison
  return (this.field1 == this.field2) && ...; 
}


public override int GetHashCode()
{   // Aim to get a unique number from the objects state 
    int tmp = ((int)(field1 << 16) + field2) ... ;
    return tmp;
}

In general it is not recommended to override the '==' and '!=' operators for reference types. They should only be overriden for immutable types. However here is a suitable implementation:
public static bool operator ==(XXX obj1, XXX obj2)
{
// IF obj1 is null
//  so obj2 must be for equality
// ELSE obj1 is not null,  
//  compare it with obj2 using above Equals() operator
    if (ReferenceEquals(obj1, null))        
        return ReferenceEquals(obj2, null); 
    else                                    
        return obj1.Equals(obj2);           
}

public static bool operator !=(XXX obj1, XXX obj2)
{
    return !(obj1 == obj2);
}
For VALUE types
public override bool Equals(object rhs)
{
    if (rhs == null) // this cannot be null
        return false;

    return Equals(rhs as XXX);
}

public override bool Equals(XXX rhs)
{
    return CompareFields(rhs);
}


private bool CompareFields(XXX p)
{ // Field by field comparison
  return (this.field1 == this.field2) && ...; 
}

public override int GetHashCode()
{   // Aim to get a unique number from the objects state 
    int tmp = ((int)(field1 << 16) + field2) ... ;

    return tmp;
}

// For VALUE types
public static bool operator ==(XXX lhs, XXX rhs)
{
    bool res = lhs.Equals(rhs);
    return res;
}

public static bool operator !=(XXX lhs, XXX rhs)
{ 
    return !(lhs==rhs); 
}

Here is a Unit Test To Test the Equals operator Create 'x', 'y' and 'z' that are the same and an object 'a' that is not

TestEqualsOperator()
{
  // Ensure all the XXX objects have the same constructor 
  // except the one called 'different'
  XXX a = new XXX(...);
  XXX x = new XXX(...);
  XXX y = new XXX(...);
  XXX z = new XXX(...);
  XXX different = new XXX(...); 

  Debug.Assert(x.Equals(x) == true);
  Debug.Assert(x.Equals(y) == y.Equals(x));
  Debug.Assert((x.Equals(y) && y.Equals(z)) && x.Equals(z));
  Debug.Assert(x.Equals(null) == false);

  Debug.Assert(a.Equals(different) == false);
  Debug.Assert(different.Equals(a) == false);
  Debug.Assert(x.Equals(different) == false);
  Debug.Assert(different.Equals(x) == false);
  Debug.Assert(y.Equals(different) == false);
  Debug.Assert(different.Equals(y) == false);
  Debug.Assert(z.Equals(different) == false);
  Debug.Assert(different.Equals(z) == false);

}

No comments: