Typically we don't really pay much attention to objects when writing our applications. For e.g. if we are designing a school system where student objects are going to used, we typically define our Student class as:
class Student { public int Grade { get; set; } public int Roll { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
Now lets say somewhere in the application a list of student objects are stored:
List<Student> students = new List<Student>(); students.Add(new Student { Roll = 1, Grade = 1, FirstName = "Neel", LastName = "Barman" }); students.Add(new Student { Roll = 2, Grade = 1, FirstName = "Arjun", LastName = "Ramesh" }); Student neel = new Student { Roll = 1, Grade = 1, FirstName = "Neel", LastName = "Barman" }; Console.Write("Is Neel in the list? {0}\r\n", students.Contains(neel));
So what's the answer to the above question? It is False when clearly it should have been true.
One way to get around is to explicitly check the business condition that if a student has the same roll-number in the same grade, the student must be the same.
Console.Write("Is Neel in the list? {0}\r\n", students.Where((s) => s.Roll == neel.Roll && s.Grade == neel.Grade).Count() == 1 );
Yes, this works but something really bad has happened. The business logic for determining if the student is the same has now crept outside, this means every single place where such a comparison has to be made the code must be aware of this rule.
So how do we fix it? Answer is simple, we need to override the Equals(object obj) method of System.Object. Another thing to remember is that when one overrides the Equals method .NET recommends we override the GetHashCode() method too. Here is the improved code:
class Student { public int Grade { get; set; } public int Roll { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public override bool Equals(object obj) { if (obj == null) return false; if (obj.GetType() != GetType()) return false; Student other = (Student)obj; return Grade == other.Grade && Roll == other.Roll; } public override int GetHashCode() { return Roll ^ Grade; } }
If we now run the code to test if the object exists in the list, we get True in both cases. What if we now test our objects ourselves students[0] == neel?
Console.WriteLine("Do we have the same student? [{0}]", students[0] == neel);
The answer is False! It should be true. You guessed it, checking via == isn't using the Equals() implementation. To get this working we need to provide custom implementation for == operator.
public static bool operator == (Student lhs, Student rhs) { return lhs.Equals(rhs); } public static bool operator !=(Student lhs, Student rhs) { return !lhs.Equals(rhs); }
As you see we have implemented both == as well as != operator because if you override one, you need to override the other also. Ok, now after all this what if we still want to check if student[0] and neel point to the same object? For that we have the Object.ReferenceEquals() method - this method works by comparing the memory addresses of the actual objects; clearly student[0] and neel are two different objects and therefore the code below correctly prints False.
Console.WriteLine("Do we have the same object? [{0}]", object.ReferenceEquals(students[0], neel));