Click here to Skip to main content
13,770,697 members
Click here to Skip to main content
Add your own
alternative version

Stats

8.8K views
120 downloads
11 bookmarked
Posted 2 Aug 2017
Licenced CPOL

Implement Equality for Value Types in C#

, 21 Jun 2018
Rate this:
Please Sign up or sign in to vote.
This post is a guide for beginners on how to implement Equality for a Value Type and why you should implement it.

Introduction

This post is to focus on implementing Equality in Value Types, i.e., overriding Equality behavior for our own implemented Value Types which is basically defining how to decide two objects of a type are equal or not.

Background

As we are now aware of the fact that the technique for checking equality for Value Types and Reference Types is different, it would be better if we discuss both of them separately. That’s why we will be focusing on each of them in separate posts to have a clear understanding on how we can override the Equality for both of these.

This post will particularly focus on the Value Types and for Reference Types we will see in some future post.

Why We Need It?

The first question that might come to mind is “Why we should override the Equality for Value Type”. We will see with a simple example and it will help you understand the fact that overriding the Equality behavior for Value Types is always a good idea indeed. We will define a struct later down the road and will implement the Equality of it.

Previous Posts on the Topic

You might want to read the previous posts in this series, if yes, following are the links to the previous content related to it:

Steps to Implement Equality for Value Type

For overriding the Equality for a Value Type, there are some necessary steps that should be performed which are mentioned below.

We will need to:

  • override the virtual Equals method of Object
  • implement IEquatable<T> interface and provide the implementation of Equals() method of it
  • provide the overloaded implementation for <span style="font-size: small">==</span> and != operator method
  • override the GetHashCode method of Object class

Possible Reasons for implementing Equality

So before we start discussing how we can implement Equality for a Value Type, let’s think a little on what are the possible reasons that would make us think that we should define our own Equality behavior for a Value Type instead of using the default one that framework already provides.

So why would you want to override it? Following are the main reasons for that:

  • We want to be able use == operator for comparing the objects of our Value Type. If you remember, we discussed previously that == operator does not work for value types and to make it work, we need to do some implementation (i.e., overload == operator) in our particular Value Type.
  • We have also seen previously that the framework provided implementation uses Reflection for checking the values of each field of that type which is obviously affecting the performance as Reflection is slow which results in poor performance of code.
  • We also sometimes need different behavior for comparison of a particular type, though this is not usually required but can be needed in some cases, as the default behavior for Value Types considers two objects to be equal if all of their fields also have the same content which is absolutely fine most of the time.

It is a recommended approach to implement Equality for your own defined Value Types that would be used further in your code base. Other developers using your defined Value Type would not get surprised or frustrated when trying to compare two objects of it by using == operator and came to know that they can’t do it.

We can skip overriding the equality for those types that we know we will be using internally in the code base and won’t be exposed to complete code base for usage and we know what we wouldn’t be needing to compare the objects frequently, so implementing equality for those types is not that much needed and would be a waste of time as a result.

Example

We will create an example struct named Employee to illustrate about implementing Equality for a Value Type. This is how our Employee struct looks like now:

public struct Employee
{
    public string Name { get; }

    public Gender Gender { get; set; }

    public Department Department { get; set; }

    public Employee(string name, Gender gender, Department department)
    {
        Name = name;
        Gender = gender;
        Department = department;
    }

   public override string ToString()
    {
        return Name;
    }
}

public enum Department
{
    HumanRecource,
   QualityAssurance,
    SoftwareDevelopment,
    ProjectManagement,
    ITOperations
}
public enum Gender
{
    Male,
    Female

We have a very simple Employee type as example which has a String property which will contain the name of employee and one enumeration property to hold the gender of employee and another enumeration for Department which will be used to track the Department of the employee. We will define our own implementation later to dictate how to decide if two employee instances are equal or not.

There are multiple things that we need to take care and implement in our Employee type for implementing Equality. The first thing that we need to do is override the Object.Equals() method so that instead of default implementation getting invoked which is obviously slow because of Reflection involved, by providing implementation for this method it will enhance the performance of the Type, so overriding Equals method will eliminate the equality check using Reflection and will make it efficient and we will be able to see significant performance improvement.

But wait, Object.Equals() method takes parameter of type Object which means that boxing will happen and we know it also hurts the performance of our code, as boxing and unboxing has its own cost involved, so here we would want to avoid that one as well some way.

For avoiding both Reflection and boxing, we will need to implement the IEquatable<Employee> interface for our type Employee. Now we will have efficient implementation of Equals() method in comparison with the framework provided one and another plus point of doing this is that now we have a much better implementation which is also type safe.

We will need to provide the overloaded implementation for == and != operators as it is normally considered a good practice to do all of the following things when overriding Equality:

  • Object.Equals method overriding
  • Implementing IEquatable<T> interface for that type
  • Implementing == and != overloaded methods
  • Object.GetHashCode method overriding

This is important to do because it will make sure that checking for equality for two objects of that type will give the same result, otherwise it can be confusing for the calling code who will be utilizing our type and can cause problems in future.

For example, if we just implement the overloads for == and != operator and we don’t provide the override for Object.Equals method, then what will happen is that the result of Equals method can be different from the result of == operator which will be troublesome for the code that will be utilizing our types.

There is also another method in Object class called GetHashCode, whenever we override the Object.Equals method for a type, then another thing that must be done is overriding the GetHashCode method as well.

Step 1 - Implementing IEquatable<T> Interface

Now let’s jump in to the Visual Studio and let’s start implementing the IEquatable<Employee> interface for our Person type. We will need to inherit our struct from IEquatable<Employee> and using the code refactoring feature of Visual Studio:

It will add the Equals method without any implementation for the IEquatable<Employee> which would look like:

public bool Equals(Employee other)
{
    throw new NotImplementedException();
}

Now we just need to provide the logic which would be used to identify that the two objects of Employee are equal or not which for this particular example we will do by checking the name and department of the employee is the same, or we can also add the Gender if we like as this is just for getting an understanding of how we can implement it so it doesn’t matter much.

After implementing the Equals method, here is how the method looks like:

public bool Equals(Employee other)
{
    var IsEqual = this.Name == other.Name && this.Department == other.Department;

    return IsEqual;
}

So the two objects of Employee will be considered Equal if they both contain in Name field and Department field same value. Our one field is of type String which does the value equality for == operator and the other is Enum which is a primitive type in C# so we know that in case of primitive type also == operator checks for value Equality, so both operations will check for value equality which is what we are trying to do here.

Step 2 - Overriding Object.Equals Method

As we are done with the IEquatable<Employee> part, let’s now override the Equals method so that it also returns the same result that we would get when checking for equality via IEquatable<Employee>, the implementation for which would be:

public override bool Equals(object obj)
{
    var IsEqual = false;

    if(obj is Employee)
    {
        IsEqual = Equals((Employee)obj);
    }

    return IsEqual;
}

What we are doing here is that first we need to make sure that the object that is passed in as parameter is of type Employee which totally makes sense as Object.Equals is not type safe and it is possible to pass an object of type Person instead of Employee and the compiler will not give any compile time error but the code would fail at run-time in that case, but adding that if block would save us from breaking the code at run-time and the method will simply return false which is the correct result as object of type Person and Employee can never be equal.

The thing to note here is that this Object.Equals override will be less efficient than Equals method implementation of IEquatable<T> because the former one has cost of Boxing when it will get called and then we are unboxing it back to Employee which IEquatable<Employee> implementation saves us from these.

What we can do is try to always call using the IEquatable<T> method, if we are concerned about performance and memory cost.

Step 3 - Overloading == and != Operator

Now let’s also implement the == and != operator for Employee which would make sure that checking for Equality using these will return consistent result what we were getting using Object.Equals or IEquatable<Employee> Equals method. So let’s add the implementation for == operator first which is:

public static bool operator ==(Employee employee,Employee otherEmployee)
{
    var IsEqual = employee.Equals(otherEmployee);

    return IsEqual;
}

The method is pretty simple, it is also reusing the Equals method implementation that we did for IEquatable<Employee>, if we build the code, it would not build and will give an error saying that it hasn’t found the implementation of != operator, in C# if we overload == operator for a type, it is mandatory to provide the implementation for inverse of it, i.e. != operator implementation as well, otherwise we will get the following compile time error:

Error CS0216: The operator 'Employee.operator ==(Employee, Employee)' requires a matching operator '!=' to also be defined

If we have overloaded any one of the operators, either == or != we would need to implement the other one as well, so let’s implement the other one as well:

public static bool operator !=(Employee employee, Employee otherEmployee)
{
   var IsNotEqual = !employee.Equals(otherEmployee);
   return IsNotEqual;
}

We can see that it is just inverting the result returned the Equals method of IEquatable<Employee> and passing it back to the caller. Now if we build our solution again, we will be able to build it successfully without any errors.

Step 4 - Implementing GetHashCode

It is a practice and considered mandatory that if we override the Equals method for a type, then we must also provide the overridden implementation for GetHashCode().

If we peek into the implementation of Object, we will see that there is a virtual method present in it named GetHashCode, its purpose is to return the 32-bit hash of the value which is contained in the object itself.

You might be wondering what is the purpose of this code, the GetHashCode method is used by types that internally use HashTables to store the objects which are normally Collections so they consume this method and get the hash value of the object to be used in the hash table as HashTables utilize the hash codes of the objects. In Framework Class Libraries, the Dictionary<TKey,TValue> also uses HashTable internally.

Hash Codes is a complete topic on which a full article can be written to cover its aspects and we will not be focusing in detail on it. How Hash Tables work is that if two objects return true when called Equals method on them, then the Hash Codes for both objects should also be returned same when we call GetHashCode method on both which in our case would mean that the following line should return true as well.

So what it actually means is that If <span style="color: #444444"><span style="color: black">employee.Equals(OtherEmployee</span>)</span> results in true, then employee.GetHashCode() == otherEmployee.GetHasCode() should also return true as result. If the GetHashCode method is not implemented how it is required, then the using Dictionary on our type wouldn’t work properly and can cause problems.

Now let’s add the implementation for our Employee type GetHashCode method which would be:

public override int GetHashCode()
{
    return Name.GetHashCode() ^ Department.GetHashCode();
}

So it is quite simple to understand that what we are doing above, we are just taking the HashCode for our String field Name and Department Enumeration and combining them using XOR as both the framework provided types so there is already implementation presentation for generating the Hash Code for these types, so we are just reusing the Hash Codes that are already available and provided by framework.

Now let’s add some code in the Main method to verify what we have implemented is working as expected, add the following code in the Main and run it:

  static void Main(string[] args)
  {
      Employee ehsan = new Employee("Ehsan Sajjad", Gender.Male, Department.SoftwareDevelopment);
      Employee ehsan2 = new Employee("Ehsan Sajjad", Gender.Female, Department.SoftwareDevelopment);

      Employee bilal = new Employee("Bilal Asghar", Gender.Male, Department.QualityAssurance);

      object ehsanObj = ehsan;

      Console.WriteLine(ehsan.Equals(ehsan2));
      Console.WriteLine(ehsanObj.Equals(ehsan2));
      Console.WriteLine(ehsan == ehsan2);

      Console.WriteLine(ehsan.Equals(bilal));
      Console.WriteLine(ehsanObj.Equals(bilal));
      Console.WriteLine(ehsan == bilal);

      Console.ReadKey();
}

The following is the result which was printed on the Console:

We can observe that for ehsan and ehsan2 objects, all the three conditions have evaluated to return true which of course means that both are equal which was expected as this is how we defined to check for equality in our type, though we defined Gender field different in both objects but it has evaluated that both are equal as we are not considering Gender field when deciding that if the two are equal or not.

Summary

Following are the points that we learned in this post:

  • We learned how to provide Custom implementation for Equality checking of Value Types
  • Implementing Equality for Value Types is normally good to do as it would improve performance by eliminating boxing/unboxing and reflection cost which can make our code inefficient.
  • There are multiple things to take care of when overriding equality for Value Types which includes:
    • Implementing IEquatable<T> interface of that Value Type which will have type safe Equals method for checking two object of a type T
    • Overriding the Object.Equals method which in turn calls the Equals method which we implemented for IEquatable<T>
    • Implementing the == and != operator overloads which also call the Equals method on IEquatable<T>
    • Implementing GetHashCode of Object for our value type T
  • This is the recommended approach for implementing Equality for Value Types and this is not the recommended way in case of Reference Types.

Might Also Like to Read

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ehsan Sajjad
Software Developer
Pakistan Pakistan
Ehsan Sajjad is a Microsoft Certified Professional, Microsoft Certified C# Specialist and he is also among the top users on StackOverflow from Pakistan with 39k+ reputation at time of writing this and counting.

He is a passionate software developer with around 5 years of professional experience in Microsoft Technologies both web and desktop applications and always open to learn new things and platforms especially in mobile application development and game development.

Some Achievements :

  • 5th Top Contributor from Pakistan on Stackoverflow.com
  • Top User in ASP.NET MVC from Pakistan on Stackoverflow.com
  • 21st June 2017 - Article of the Day - ASP.NET Community (Beginners Guide on AJAX CRUD Operations in Grid using JQuery DataTables in ASP.NET MVC 5)
  • 19th April 2017 - Article of the Day - ASP.NET Community (ASP.NET MVC Async File Uploading using JQuery)
  • March 2017 - Visual C# Technical Guru Silver Medal on Microsoft Tech Net Wiki Article Competition
  • 20 January 2017 - Article of the Day - ASP.NET Community (Async File Uploading in ASP.NET MVC)
  • 22nd September 2016 - Article of the Day - ASP.NET Community (GridView with Server Side Filtering, Sorting and Paging in ASP.NET MVC 5)
  • 22nd August 2016 - Article of the Day - ASP.NET Community (Beginners Guide for Creating GridView in ASP.NET MVC 5)
  • December 2015 - C-SharpCorner Monthly Winner

You may also be interested in...

Comments and Discussions

 
Questionfor C# Pin
Member 1388155921-Jun-18 4:10
memberMember 1388155921-Jun-18 4:10 
AnswerRe: for C# Pin
Ehsan Sajjad22-Jun-18 4:53
mvpEhsan Sajjad22-Jun-18 4:53 
QuestionWhat do you recommend for doubles and equality? Pin
asiwel3-Aug-17 10:25
professionalasiwel3-Aug-17 10:25 
AnswerRe: What do you recommend for doubles and equality? Pin
Ehsan Sajjad5-Aug-17 11:07
mvpEhsan Sajjad5-Aug-17 11:07 
GeneralRe: What do you recommend for doubles and equality? Pin
asiwel5-Aug-17 11:39
professionalasiwel5-Aug-17 11:39 
GeneralRe: What do you recommend for doubles and equality? Pin
Ehsan Sajjad7-Aug-17 1:39
mvpEhsan Sajjad7-Aug-17 1:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.181119.1 | Last Updated 21 Jun 2018
Article Copyright 2017 by Ehsan Sajjad
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid