Object Identity vs. Object Equality – Overriding System.Object.Equals(Object obj)

Raymond and Eric have been discussing object-oriented programming ( Object Oriented Programming VB .NET, Encapsulation, Data Abstraction ), which is fantastic because I love to talk about OOP in the hopes that I will learn more myself.


To continue the subject, when you create new classes in .NET, you need to consider what it means for two instances (objects) of your classes to be equal.


By default, your class inherits from System.Object.  System.Object has a virtual method Equals that your instances will inherit if you choose not to override the method.


System.Object’s Equals Method

public virtual Boolean Equals(Object obj) {

// If both references point to the same
// object, they must be equal.
if (this == obj) return true;

// Assume that the objects are not equal.
return false;
}


System.Object takes a simple view of object equality and just tests if two objects are the same instance (e.g. share the same address in memory), which is often referred to as Object Identity. If the objects being compared are the same instance, they are considered equal.  If they are the not the same instance, they are considered not equal.


If the above test for equality is fine for your class, I recommend the simplest course of action – do not override the method.


However, many times the test for equality between two objects is not about referential semantics, but value semantics.  In other words, equality may mean more about the objects having the same field values and not that they are the same instance.


For example, let’s take System.String as an example.  When a user logs into your application, you often need to compare the supplied password with the stored password for equality:



Comparing Passwordsif (storedPassword.Equals(suppliedPassword))
   


Now obviously in this case you are not testing if “storedPassword” and “suppliedPassword” are the same instances (Object Identity).  You want to test if “storedPassword” and “suppliedPassword” have identical characters.  Lucky for us that System.String has overriden Equals to do that very thing.


If in actuality you did want to test that storedPassword and suppliedPassword were the same instance, you can do this via the static Object.ReferenceEquals(Object obj1, Object, obj2) method, which is a way for you to test if two objects indeed are the same instance and makes it easier to understand in code your intentions:



Testing for Object Identityif (Object.ReferenceEquals(storedPassword, suppliedPassword))
   


In my Customer Class below, I decided that two customer objects were indeed equal when their member variables had the same values, not if they were just the same instance.  For simplicity, my Customer only has one member variable, _id, which in my case is guaranteed to be unique across all my customers.



Customer Classpublic class Customer : Object {


    private readonly string _id;


    public string ID
    {
       
get { return _id; }
    }


    public Customer(string id)
    {
       
if (id == null || id.Length == 0)
           
throw new ArgumentNullException(id);

        _id
= id;
    }


    public override string ToString()
    {
       
return _id;
    }


    public override bool Equals(object obj)
    {
       
if (obj == null) return false;
       
if (Object.ReferenceEquals(this,obj)) return true;
       
if (this.GetType() != obj.GetType()) return false;


        Customer objCustomer = (Customer)obj;
       
if (_id.Equals(objCustomer.ID)) return true;


        return false;
    }


    public override int GetHashCode()
    {
       
return _id.GetHashCode();
    }


}


In my override of Equals, I essentially go through a number of basic checks to verify equality.



  • If “obj” is null, they are obviously not equal.
  • If the objects are the same instance, they must be equal.
  • If the objects are not the same type, they cannot be equal.
  • And for me, if the ID’s are the same, I consider them equal.
  • Otherwise, they are just not equal.

To quote “textbooks,” there are some mathematical rules that you must adhere to when overriding Equals.  Essentially, equality is reflexive, symmetric, and transitive.  I always forget how to explain those terms, so I had to pull out Applied Microsoft .NET Framework Programming by Jeffrey Richter to get it straight from an expert-



  • Equals must be reflexive; that is, x.Equals(x) must return true;
  • Equals must be symmetric; that is, x.Equals(y) must return the same as y.Equals(x).
  • Equals must be transitive; that is, if x.Equals(y) returns true and y.Equals(z) returns true, then x.Equals(z) must also return true.
  • Equals must be consistent. Provided there are no changes to the two values being compared, Equals should consistently return true or false.

One last note, if you override Equals you must override GetHashCode().  GetHashCode() is used to define the hash value for keys in hash-based collections, like a Hashtable or Dictionary container.  In this case, I just called GetHashCode on my _id, which cannot be changed during the life of the object and is thus a “safe” method of generating a HashCode for this class – albeit I am a little fuzzy as to if this would generate an ideal random distribution for all classes (probably depends on the values of id).  Anyone have any advice on a better GetHashCode() Function for this example or anything else?

This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to Object Identity vs. Object Equality – Overriding System.Object.Equals(Object obj)

  1. Have a read of my post on GetHashcode(), Equals(object obj) and the ‘==’ operator.

    http://blogs.wdevs.com/angelos/archive/2004/12/21/1553.aspx

  2. Agreed! Good Post. I actually did a poor job delving into this a while back, I really didn’t understand it at the time:

    http://codebetter.com/blogs/brendan.tompkins/archive/2004/07/09/18700.aspx

    Which solicited this statement from none other than Raymond Chen:

    "The hash function must return exactly the same value regardless of any changes that are made to the object."

    Since you’re using your ID (which shouldn’t change) for your hashcode, you’re doing things right. I think. :)

  3. Great post David. This was something I was planning on covering in the future, but don’t think I would have covered it this well. Wonderful explanation on a topic that slightly confused me 3 years ago. Also, that .Net Framework Programming is a great book. I always have it right here handy.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>