Story of Equality in .NET – Part 5

0
28

Introduction

I hope that after reading the previous posts, you now have a better understanding of how the C# equality operator works for primitive types like int, float, double, etc. In this post, we will be focusing on how the C# equality operator and Equals method behave for reference types, either they are framework class library types or user defined custom types.

Background

This article is in the continuation of series of articles regarding how Equality works in .NET. The purpose is to have the developers more clear understanding on how .NET handles equality for different types. If you haven’t read the previous parts and are interested in reading from start about this topic, you can navigate all parts published before here:

In the previous post (i.e. Part 4), we compared the result of equality comparison of value type using == operator and Object.Equals method and the result was the same but the mechanism for evaluating the equality is different as the IL generated for Object.Equals and == operator was different, which means that == operator does not call Object.Equals behind the scenes, but it uses CPU’s registers to determine if the two value type variables are equal or not.

 

== Operator and Reference Types

If you recall from the previous post, we saw an example using reference types for equality that it checks for reference equality. Now we will modify the same example to see that equality operator compiles to what in case of reference types.

class Program { static void Main(String[] args) { Person p1 = new Person(); p1.Name = "Ehsan Sajjad"; Person p2 = new Person(); p2.Name = "Ehsan Sajjad"; Console.WriteLine(p1.Equals(p2)); Console.WriteLine(p1 == p2); Console.ReadKey(); } }

Now, we will test using both these method of checking equality, i.e., equality operator of C# and Equals method of Object type. If we run this example, we will see false printed on the console two times which was expected result as Person is a reference type and those are two different instances of person with difference memory reference.

From the output, we can easily deduce that both the equality operator and Equals method check for reference equality for reference types and that’s actually what’s happening. So, Equality operator also checks reference equality, not value equality for reference types, just same like Equals method.

What Happens Behind the Scenes?

Now let’s examine the IL code generated for this example. For doing that, open the Visual Studio command prompt, for opening it, go to Start Menu >> All Programs >> Microsoft Visual Studio >> Visual Studio Tools>> Developer Command Prompt.

Type ildasm on the command prompt, 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 for installing it.

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

The IL code for the above C# code looks like:

If we see the IL code for p1.Equals(p2), there are no surprises, it is comparing the equality by calling the virtual Equals method of Object, and the method signatures are pretty clear, as they say it requires an object, so this is actually a virtual call to Object.Equals method, this is the best type’s method match that C# compiler picked.

Now if we look at IL code generated for equality operator comparison of the objects, it is exactly the same instruction used which we saw for the integer example in the previous part. It is not calling any method to do the comparison, it is just loading both arguments to the evaluation stack and doing a ceq, which is a dedicated IL instruction to check for equality probably using the CPU’s hardware.

You might be thinking how does that achieve the reference equality? We know that Person is a class which is a reference type which means whatever the variable of type Person contains is the address in memory of where the Person object is on managed heap.

Both arguments p1 and p2 are holding the memory addresses and you know addresses in memory are just numbers which means that they can be compared using the ceq statement for equality just like the integers you declare in the code. In fact, this ceq is comparing the addresses to see if they are equal, in other words whether the reference is pointing to the same memory address is reference equality.

Summary

  • We saw that == operator and Object.Equals method call both work differently behind the scenes which we can verify by inspecting the IL code generated.
  • We saw that for Reference types as well using == operator gives us the same result as calling Object.Equals but underlying mechanism of == operator is different in IL as compared to Object.Equals, which is that it does not uses the Object.Equals, instead it uses ceq instruction which does the comparison of memory addresses using hardware instructions.

You Might Also Like to Read

LEAVE A REPLY