Story of Equality in .NET - Part Four

Background

This article is the continuation of the previous three articles about how Equality works in .NET. The purpose is to give the developers; a clear understanding on how .NET handles equality for the types. You may want to read the previous post as well, whose links are stated below-

Introduction

I hope that after reading the previous three posts, you now have an understanding how the .NET framework deals with Equality, using the virtual Object.Equals method for most of the value types and for a few reference types, using IEquatable<T>. In this post, we will be discussing C# equality operator, provided by Microsoft. We will explore, what the C# Equality operator does and how it works. After reading this post, I hope, you will have a better understanding, what it means, when you check for equality of the two variables, using == operator.

We will cover the following things-
  • We will be writing some code to compare the == operator and Object.Equals behavior for the same parameters to see what happens and we will see that the result is the same, but the mechanism used for both are different.
  • We will see, how C# equality operator works for the primitive types.

C# also provides the inequality operator as well; the syntax for which is !=. We will not be discussing this operator in detail, because it is the negation of what the equality operator does. For example, if a==b evaluates to be true, a!=b should evaluate to false and vice versa. Apart from this difference, both the operators work exactly the same way. Thus, keep in mind that everything that we will discuss in this post about the equality operator, which also applies to the inequality operator, as it will just inverse the return value.

The equality operator is used, when we want to evaluate, if two of the variables are equal or not. A lot of developers have a misconception that this operator is basically the same as Object.Equals method and it is just a syntactical convenience provided by C# language.

This is not true actually. It is being designed in a way that it often gives the same result, as given by calling Object.Equals but that’s not always the case, as the underlying mechanism is completely different.

== Operator and Primitive Types

We will see with the example code, how the equality operator uses the different mechanism in reference to Object.Equals method.

  1. class Program {  
  2.     static void Main(String[] args) {  
  3.         int num1 = 5;  
  4.         int num2 = 5;  
  5.         Console.WriteLine(num1.Equals(num2));  
  6.         Console.WriteLine(num1 == num2);  
  7.         Console.ReadKey();  
  8.     }  
  9. }  
We are comparing two integers for the equality, using both ways i.e first by using Object.Equals overload for an integer and the second one , using C# equality operator. We will examine the generated IL code, which will help us to understand, how they are different in mechanism.

Of course, when we will run this program, it will evaluate to be true for both the statements and you can test it on your machine as well. As the result of both the statements is the same, this makes us believe that both are using Object.Equals and checks two integers for an equality.

What Happens Behind the Scenes

As we talked about earlier the  == operator and Object.Equals, which works differently and we are going to see, how it will be a proof to what we talked about earlier. We will examine the IL, generated for both the statements after the compilation.

One thing to note here is that before doing this, you will need to build your project, using Release build not debug, as a debug code will generate a lot of unnecessary instructions, which are helpful, when we need to debug and also in debug, build it will use Object.Equals implementation for both, so that it will not help you to see, what we will discuss next.

For doing this, open Visual Studio command prompt. To open it, go to Start Menu >> All Programs >> Microsoft Visual Studio >> Visual Studio Tools>> Developer Command Prompt.

 Developer Command Prompt

Type ildasm on the command prompt and this will launch the ildasm, which is used to look at the IL code, contained in an assembly. It is installed automatically, when you install Visual Studio, so you don’t need to do anything to instal it.

ildasm

Browse the folder, where your executable exists and open it, using File Menu. This will bring up the IL code of your executable.

folder

Expand the Program class and double click the Main method. It will open up the intermediate language for Main method.

method

You don’t need to understand all the code written in it. If you have not seen IL before, it may look complex to you, but you don’t need to understand all the instructions.

We will just look at the lines, where the comparison is done both ways to show you the difference, you can see the line, given below-

IL_0007: call instance bool [mscorlib]System.Int32::Equals(int32)

Here, it is calling the Object.Equals implementation, provided via IEquatable<int> for an integer type. In IL, we need to specify the method call, using Fully Qualified name, so the statements, mentioned above say to call Equals method, which takes int32 as a parameter and method exists in System.Int32 type. This type exists in mscorlib assembly.

Now, look at IL generated for the second comparison, which was done via equality operator, which is-

IL_0013: ceq

You can see that the call to Equals method in Int class is not called in this case. Instead, we have an IL instruction written ceq , which says to compare the two values, that are being loaded on the stack right now and perform an equality comparison, using CPU registers. Thus, C# equality operator uses CEQ statement to do equality check for the primitive types and it does not call the Object.Equals implementation , provided by that primitive type.

type

Summary 
  • We compared the == operator with Object.Equals method in this post.
  • We saw that using == operator gives us the same result as calling Object.Equals, but the underlying mechanism of == operator is different in IL, as compared to Object.Equals, which is it does not use the Object.Equals. Instead, it uses CPU registers to do the comparison.