Comparing Complex Type With ==, Equals, IEquatable and IComparable in C#

In C# if we want to compare two variables then we can use the == operator. We can also use the Equals method for that but if we want to compare two reference types then we need to do something different. This is because in value types we can compare variables by their value but in the case of complex type objects that are reference types and the types may contain many fields or properties we need to define something by which we can compare the property or member values. One method uses reflection but that method is a bit heavy and performance of the application will suffer. However if a reference of two objects are the same then we can compare them using the == operator.

If the reference of two complex types is the same then they are equal but sometimes we encounter a scenario where the reference of two objects is different but still we need to compare types based upon their fields and properties.

Difference between == and Equals Method

== compares the reference equality whereas the Equals method compares only the content. The == operator does not throw an error with null values whereas the Equals method throws an error if the first value to compare is null. However we can override the Equals method and change its functionality depending on our requirements so that we are able to compare two complex types also.

In the following example I have created a class Student in which I have overridden the Equals method and the GetHashCode method.

  1. public class Student  
  2. {  
  3.     public int Id { get; set; }  
  4.     public string Name { get; set; }  
  5.   
  6.     public override bool Equals(object obj)  
  7.     {  
  8.         Student student = (Student)obj;  
  9.         return (this.Id == student.Id && this.Name == student.Name);  
  10.     }  
  11.   
  12.     public override int GetHashCode()  
  13.     {  
  14.         return this.Id.GetHashCode() ^ this.Name.GetHashCode();  
  15.     } 

Now if we create two instances of the preceding class:

  1. Student first = new Student();  
  2. Student second = new Student();  
  3. Console.WriteLine(first == second); // returns false  
  4. Console.WriteLine(first.Equals(second)); // returns true 

The fourth line returns true because we have overridden the method to compare the properties of the class. If we have not implemented these methods then the fourth line will return false.

Note: It is important to override the GetHashCode method because if our object (of the student class) is a key of Dictionary or Hashtable (that calculates the HashCode of the key) then in that case the Equals method will not provide expected results.

IEquatable vs. IComparable interface

IEquatable and IComparable are interfaces that are used to compare complex type objects. The IEquatable interface is generally used for checking the equality of two complex types whereas the IComparable interface is used for comparing two objects and it tells us whether the first object is greater than or less than the other. IEquatable is generally used in cases where we want to compare two objects whereas IComparable is used in the case of the sorting of list of objects.

Using IEquatable Interface

The IEquatable interface is used to compare two objects by providing the Equals method. It is similar to the Equals method of the Object class (that we have discussed in the preceding), the only difference between the two is that the Equals method of The IEquatable interface is a generic method and it avoids the boxing and unboxing of objects that improves the performance.

  1. class Employee : IEquatable<Employee>  
  2. {  
  3.     public int Id { get; set; }  
  4.     public string Name { get; set; }  
  5.   
  6.     public bool Equals(Employee other)  
  7.     {  
  8.        return (this.Id == other.Id && this.Name == other.Name);  
  9.     }  

Note: When implementing the IEquatable interface it is suggested that we should also override the Equals method of the object class since any user can also call the Equals method by passing an object type. For example if we create two instances of an Employee class and compare two objects.

  1. Employee ob1 = new Employee();  
  2. Employee ob2 = new Employee();  
  3. Console.WriteLine(ob1.Equals(ob2)); 

Then the code above will return true since the properties of both objects have the same value. But if we create objects as in the following:

  1. Employee a = new Employee();  
  2. object b = new Employee();  
  3. Console.WriteLine(a.Equals(b)); 

Then we will get unexpected results because in this case our Equals method is not called. So the better approach is to override the Equals method of the Object class also when implementing the IEquatable interface.

  1. class Employee : IEquatable<Employee>  
  2. {  
  3.     public int Id { get; set; }  
  4.     public string Name { get; set; }  
  5.   
  6.     public bool Equals(Employee other)  
  7.     {  
  8.        return (this.Id == other.Id && this.Name == other.Name);  
  9.     }   
  10.     public override bool Equals(object obj)  
  11.     {  
  12.        Employee employee = (Employee)obj;  
  13.        return (this.Id == employee.Id && this.Name == employee.Name);  
  14.    }   
  15.    public override int GetHashCode()  
  16.    {  
  17.       return this.Id.GetHashCode() ^ this.Name.GetHashCode();  
  18.    }  

Using IComparable Interface

The IComparable interface is also used to compare two objects by giving the CompareTo method that returns an integer. If the returned value is 0 then it means two objects are equal otherwise it will return 1 if the first object is greater than the second and -1 if the first object is smaller than the second.

  1. class PermanentEmployee : IComparable<PermanentEmployee>  
  2. {  
  3.     public int Id { get; set; }  
  4.     public string Name { get; set; }  
  5.     public int CompareTo(PermanentEmployee other)  
  6.     {  
  7.        return this.Id.CompareTo(other.Id);  
  8.     }  

Note: If we do not implement this interface and if we build a List of type PermanentEmployee and call its Sort method then it will throw an error because the compiler does not know how to sort a PermanentEmployee type of object. But after implementing the IComparable interface we can sort our complex object list.

The IComparable interface has two versions, simple and generic. The generic version is preferred because it elimiinates the necessity of boxing and unboxing that affects performance.

Similar Articles