Equality in C#: Part 1- Equality types and reference equality

I had an interesting discussion with one of my students regarding equality in C# recently. It all started from a simple question: “how can I check if two objects are equal?”. Answering the question brought up some additional questions and all ended up in a crash course on equality. Therefore, I thought it might be useful for some developers to share a summary in a series of blog posts. In this first blog posts I’ll talk about the different types of equality in C# with emphasis on reference equality.

Clearly, I’m not reinventing the wheel since most of the information is already present in different documentation files. The main goal of this post is to gather in one place information that is spread across different sources. And of course filter this information in accordance with my experience.

Types of equality in C#

First of all, the whole idea of equality in C# is not necessarily very straight forward since we can have different types of equality. Why? Simply because when we ask the question about equality (also called equivalence) we often mean total different things even if we don’t realize that. In certain situations when we compare variables for equality we just want to know if they hold a reference to the same memory location, therefore to the object that is stored there. This is called reference equality.

Some other times when we compare for equality in C# we want to find out if all the properties (or a subset of them) of the objects in question have the same values or not. This is called value equality or structural equality.

Further, there might be cases when we just want to know if the identifiers of two different objects have the same value. This is what I would call identifier equality and even if it is not mentioned in the official Microsoft documentation, this is a type of equality that we deal with almost every day, for instance when we want to find a certain record in the database, or an element in a list and so on. Especially in DDD (domain driven design) the concept of identifier equality is very important since it is one of the key difference between entities and value objects.

Testing for reference equality

The good news is that you don’t actually have to implement any custom logic to support reference equality comparisons in your types. This functionality is provided for all types by the static ReferenceEquals() method as well as by the Equals() method. The latter actually calls the former when it is invoked. These are defined on the Object class and therefore all types will have them.

The following example shows how to determine whether two variables have reference equality, which means that they refer to the same object in memory.

1
2
3
4
5
static void Main(string[] args)
{
//Student class is omitted for brevity
var stud1 = new Student() {Name = "Dan", Age = 15};
var stud2 = new Student() {Name = "Dan", Age = 15};

//returns False
Console.WriteLine(stud1.Equals(stud2));

//Now the variable stud2 will have the same reference as value
stud2 = stud1;

//Returns True

Console.WriteLine(stud1.Equals(stud2));

Console.ReadLine();
}

One thing to bare in mind is that testing value types for reference equality will always result in “False”.

1
2
3
4
5
static void Main(string[] args)
{
//Student struct is omitted for brevity
var stud1 = new StudentStruct() {Name = "Dan", Age = 15};
var stud2 = new StudentStruct() {Name = "Dan", Age = 15};

//Displays: False
Console.WriteLine(object.ReferenceEquals(stud1, stud2));

//Displays False
stud2 = stud1;
Console.WriteLine(object.ReferenceEquals(stud1, stud2));

Console.ReadLine();
}

Things are not always simple

You probably noticed that I used the ReferenceEquals() method when testing two structs for equality. The result corresponds, of course, to the earlier stated principles that testing value types for reference equality will always return false. This makes a lot of sense since value types hold values not references to memory locations.

But what if we use the Equals() method?

1
2
3
4
5
static void Main(string[] args)
{
//Student struct is omitted for brevity
var stud1 = new StudentStruct() {Name = "Dan", Age = 15};
var stud2 = new StudentStruct() {Name = "Dan", Age = 15};

//Displays: True
Console.WriteLine(stud1.Equals(stud2));

//Displays True
stud2 = stud1;
Console.WriteLine(stud1.Equals(stud2));

Console.ReadLine();
}

Bummer! Equality check return “True” when using the Equals method! But why?

Well, things are fairly simple. The struct class overrides the Equals() method to check for value equality instead of reference equality. And since all properties on our struct objects have the same values the value equality check returns “True”.

Further, if you think about the applications that you write, you’ll sure realize that there are a lot of classes that are not written by you. Moreover, you probably use a lot of classes from different libraries. This means that wherever the class author wrote an override for the Equals() method that tests for value equality instead of the default check for reference equality, you’ll probably get unexpected results when comparing two objects.

That’s why it is wise that whenever you want to test for reference equality, you should use the ReferenceEquals() method on the object class instead of the Equals() method (because this one might be overridden without your knowledge). If you want to remember only one thing from this blog posts, then that thing should be this best practice!

That’s it for now. In the next article I will discuss the concept of value or structural equality and I’ll walk through some practical tips that developers need to take into consideration when implementing equality in their own C# types.

This article is part of a series on equality in C#. Here are the other articles in the series.

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

Dan Patrascu-Baba

Dan Patrascu-Baba

Developer, consultant, trainer at Codewrinkles
,Net Developer.Focusing on both the .Net world and Microsoft Azure. Experienced speaker and trainer.
Dan Patrascu-Baba
Spread the word!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.