#413 – Check for Reference Equality Using Object.ReferenceEquals

You might encounter types that override both the == operator and the Equals method to check for value equality.

For example, a PersonHeight value type might behave this way.  Both checks return true in the example below.

            PersonHeight ph1 = new PersonHeight(5, 10);
            PersonHeight ph2 = new PersonHeight(5, 10);

            // Both == operator and Equals method are checking
            // for value equality
            bool check = ph1 == ph2;    // true
            check = ph1.Equals(ph2);    // true

If you want to do a reference equality check, you can always use the Object.ReferenceEquals method. This method cannot be overridden and always does a reference equality check–i.e. compares two object references to see if they refer to the same object.

            // Check for reference equality
            check = ReferenceEquals(ph1, ph2);      // false
Advertisement

#412 – Guidelines when Overloading == Operator for a Value Type

When you overload the == operator for a value type, you should also:

  • Overload != operator
  • Override Equals method
  • Overload < and > operators

This is also true for reference types.

#411 – Overriding the Equals Method for a Value Type

A user-defined struct inherits from System.ValueType implicitly.

System.ValueType includes a default implementation of the Equals method that compares the individual fields of two instances of the type, to determine if the instances are equivalent.  It uses reflection to determine which fields to compare and compares both public and private fields.  (This includes the values of auto-implemented properties, which use an automatically generated private backing field).

You should generally override the Equals method in a user-defined struct, comparing all of the fields directly.  This is typically faster than the default implementation, since your method does not need to use reflection.

If you are also overloading the == operator, you can define Equals in terms of the operator.

        public override bool Equals(object obj)
        {
            return ((obj is PersonHeight) && ((PersonHeight)obj == this));
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }

#410 – Overloading the == Operator for a Value Type

A user-defined struct automatically inherits an Equals method that performs a value equality check by comparing each field of the struct. The == operator, however, is not automatically defined.  If you want to use the == operator for instances of a struct, you need to overload the == operator.

    public struct PersonHeight
    {
        public int Feet { get; set; }
        public int Inches { get; set; }

        public PersonHeight(int feet, int inches) : this()
        {
            Feet = feet;
            Inches = inches;
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }

        public static bool operator !=(PersonHeight ph1, PersonHeight ph2)
        {
            return !(ph1 == ph2);
        }
    }

Some test cases:

            PersonHeight ph1 = new PersonHeight(5, 10);
            PersonHeight ph2 = new PersonHeight(5, 10);

            // Returns true, default Equals method compares each field
            bool check = ph1.Equals(ph2);

            // == operator also now works - true
            check = (ph1 == ph2);

#409 – Example of Overloading the == Operator

Here’s a full example that shows how to overload the == operator for a reference type.

    public class PersonHeight
    {
        public int Feet { get; set; }
        public int Inches { get; set; }

        // Constructor goes here

        public override bool Equals(object obj)
        {
            return ((obj is PersonHeight) && ((PersonHeight)obj == this));
        }

        public override int GetHashCode()
        {
            return Feet.GetHashCode() ^ Inches.GetHashCode();
        }

        public static bool operator ==(PersonHeight ph1, PersonHeight ph2)
        {
            if (ReferenceEquals(ph1, ph2))
                return true;

            if (((object)ph1 == null) || ((object)ph2 == null))
                return false;

            return (ph1.Feet == ph2.Feet) && (ph1.Inches == ph2.Inches);
        }

        public static bool operator !=(PersonHeight ph1, PersonHeight ph2)
        {
            return !(ph1 == ph2);
        }

        // Also overload < and > operators
    }

Test cases:

            PersonHeight ph1 = new PersonHeight(5, 10);
            PersonHeight ph2 = new PersonHeight(5, 10);
            PersonHeight ph3 = null;

            bool check = ph1.Equals(null);
            check = ph1.Equals("NO!");
            check = ph1.Equals(ph2);
            check = PersonHeight.Equals(ph1, ph2);

            check = (ph1 == null);
            check = (ph3 == null);
            check = (ph1 == ph2);

#408 – Overloading the == Operator for a Reference Type

You can override the Equals method for a type to implement value equality for the type. You can also overload the == operator.

By default, the == operator will do a reference equality check, only returning true if two references point to exactly the same object.  This is normally what you want for a reference type, using the Equals method if you want to check for value equality.  But there might be cases when you want to also overload the == operator, having it also check for value equality.  You might do this if your type represents some basic value, like a complex number.

Some guidelines.  Whenever you overload the == operator, you should also:

  • Overload != operator
  • Override Equals method
  • Overload < and > operators

#407 – Why You Should Override GetHashCode when Overriding Equals

Whenever you override the Equals method for a class, you should also override the GetHashCode method.

GetHashCode is a method that provides values to use as keys in a hash table.  This value, the hash, is just a number that represents the value of an object, without having to look at the entire object.  The hash code can also be thought of as a key used to look the object up in a hash table.

Objects that are equivalent should always return the same hash code.

If you don’t implement GetHashCode for your object and the object ends up stored in a Dictionary or HashTable, it’s possible that the algorithms for sorting or finding elements in these structures will not work properly.

#406 – Overriding the Equals Method

You can override the Equals method in a custom type to implement value equality for two instances of the type.

Guidelines to follow when overriding Equals include:

  • x.Equals(x) should return true
  • x.Equals(y) should return the same value as y.Equals(x)
  • x.Equals(y) and y.Equals(z) implies that x.Equals(z)
  • Repeated calls to x.Equals(y) return the same result, for the same values of x and y
  • x.Equals(null) returns false

Additionally:

  • When you override Equals, you should also override GetHashCode

Here’s the implementation of Equals for the Dog class:

        // Are two Dogs equivalent?
        public override bool Equals(object obj)
        {
            // Can't be null
            if (obj == null)
                return false;

            // Must be a Dog
            if (obj is Dog)
            {
                // Compare the dogs
                Dog d2 = (Dog)obj;
                return (Name == d2.Name) && (Age == d2.Age);
            }
            else
                return false;
        }

Here’s the override of GetHashCode.

        public override int GetHashCode()
        {
            return Name.GetHashCode() ^ Age;
        }

#405 – Equals Method for Equivalence, == Operator for Identity

You can check for equality between two objects by calling the Equals method or by using the == operator.  There are some differences between how the methods work, but both Equals and == can be overloaded in a user-defined type.

You can therefore change the behavior of one of these methods, or both.  The question is–how should these two behaviors work for a custom class?

Roughly speaking, the desired behavior, from a client’s point of view is:

  • Use Equals method to determine value equality, or equivalence–do the two objects represent the same thing?
  • Use == operator to determine reference equality, or identity–do the two references point to exactly the same object?

This argues for overloading Equals for a user-defined reference type, but not overloading the == operator.

This would result in the following behavior:

            Dog d1 = new Dog("Lassie", 7);
            Dog d2 = new Dog("Lassie", 7);

            bool bValueEquality = d1.Equals(d2);    // true
            bool bRefEquality = d1 == d2;    // false

#404 – Equals Method vs. == Operator for Value Types

In C#, you can compare two objects for equality by using the Equality operator (==) or by calling the Equals method.  For built-in value types, the default behavior checks for value equality, or equivalence.

            int i1 = 42;
            int i2 = 42;

            bool check1 = i1 == i2;      // true
            bool check2 = i1.Equals(i2);  // true
            bool check3 = int.Equals(i2, i2);  // true

For the predefined value types (e.g. int, float):

  • The Equals method performs a value equality check
  • The == operator resolves to the CIL ceq instruction, which does a strict identity check

For user-defined struct types:

  • The Equals method implementation performs a value equality check by comparing each field of the struct (using reflection).  You can (and should) override Equals to provide an implementation specific to the struct.
  • The == operator is undefined, unless you override it in the struct.