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 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:
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:
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.
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?