Implementing the IEquatable of T interface for object equality with C# .NET
October 19, 2017 1 Comment
In this short post we’ll see a way how to make two custom objects equatable using the generic IEquatable interface. Consider the following object:
public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
The object superclass has an Equals method that we can test as follows:
Person personOne = new Person() { Age = 6, Name = "Eva", Id = 1 }; Person personTwo = new Person() { Age = 6, Name = "Eva", Id = 1 }; Console.WriteLine(personOne.Equals(personTwo));
This will obviously yield false as the Person objects are reference types. PersonOne and personTwo do not point to the same position in memory so they are deemed different.
Another problem with the Object.Equals method is that it’s not type safe. We can have a completely different object…
public class House { public double Area { get; set; } public int NumberOfRooms { get; set; } public string Address { get; set; } public bool ForSale { get; set; } public DateTime DateBuilt { get; set; } }
…and test equality with a Person:
Person personOne = new Person() { Age = 6, Name = "Eva", Id = 1 }; House house = new House() { Address = "London", Area = 123.45, ForSale = false, NumberOfRooms = 4, DateBuilt = DateTime.UtcNow.AddYears(-2) }; Console.WriteLine(personOne.Equals(house));
…the compiler won’t complain as Object.Equals accepts any object.
One way to implement equality is using the generic IEquatable interface which has only one method: Equals. We’ll say that two Person objects are equal if their IDs are equal:
public class Person : IEquatable<Person> { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public bool Equals(Person other) { if (other == null) return false; return Id == other.Id; } }
The previous code…
Person personOne = new Person() { Age = 6, Name = "Eva", Id = 1 }; Person personTwo = new Person() { Age = 6, Name = "Eva", Id = 1 }; Console.WriteLine(personOne.Equals(personTwo));
…will now print true.
However, we still have a slight problem. It’ probably unlikely but it can happen that a Person will be declared as an object:
object personOne = new Person() { Age = 6, Name = "Eva", Id = 1 }; Person personTwo = new Person() { Age = 6, Name = "Eva", Id = 1 }; Console.WriteLine(personOne.Equals(personTwo));
The compiler will now call Object.Equals, not Person.Equals as we’re passing in an object type to the Equals method. Again, it returns false. As a solution we can override object.Equals in the Person class:
public override bool Equals(object obj) { if (obj is Person) { Person p = (Person)obj; return Equals(p); } return false; }
The previous example will now return true.
One last thing. If an object overrides Object.Equals it should also override GetHashCode which will return an integer. In our case we can just return the ID as our equality is based on IDs:
public override int GetHashCode() { return Id; }
View all various C# language feature related posts here.
Reblogged this on Elliot Balynn's Blog.