Deep Compare 2 Object in C#

Beribey
6 min readAug 21, 2020

In this article, I will talk about something quite simple in C #: Compare two objects. This is a problem that everyone thinks is easy; I will gradually raise the issue from simple to complex. The workaround will also go from simple to complicated, then back to simple. If you take the time to read this article from beginning to end, you will realize many things, and the technical ability will increase quite well.

Photo by Luca Bravo on Unsplash

Level 1: Simple class

We start the problem with a simple class. This class has three properties, when we call the Equals function, C # only compares the reference, so the result is False

public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
var student1 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
Age = 15,
};
var student2 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
Age = 15,
};
student1.Equals(student2); //False

To handle this, we need to override the Equals function, and it is not complicated, everyone will have learned at school.

public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType()) return false;var student = (Student) obj;return FirstName.Equals(student.FirstName)
&& LastName.Equals(student.LastName)
&& Age == student.Age
&& BirthDate.Equals(student.BirthDate);
}

Level 2: Use the extension method

Equals function still has a shortcoming, that is student1 cannot be null. We can handle this problem by using the extension method as follows. Now we can call student1. Equals (student2) even though student1 has null:

public static bool DeepEquals (this Student obj, Student another)
{
// If null or the same, return true
if (ReferenceEquals (obj, another)) return true;

// If one of them is null, return false
if ((obj == null) || (another == null)) return false;

return obj.FirstName.Equals (another.FirstName)
&& obj.LastName.Equals (another.LastName)
&& obj.Age == another.Age
&& obj.BirthDate.Equals (another.BirthDate);
}

Level 3: Write a method to compare two objects in general

Think of your application case having several dozen classes, each with several dozen properties. What will you do? Handwriting the Equals function for each class? Use Refection in C # to write an object comparison function that can be used by any object.

public static bool DeepEquals (this object obj, object another)
{

if (ReferenceEquals (obj, another)) return true;
if ((obj == null) || (another == null)) return false;
// Comparing class of 2 objects, if different, then fail
if (obj.GetType ()! = another.GetType ()) return false;

var result = true;
// Get all properties of obj
// then compare the value of each property
foreach (var property in obj.GetType (). GetProperties ())
{
var objValue = property.GetValue (obj);
var anotherValue = property.GetValue (another);
if (! objValue.Equals (anotherValue)) result = false;
}

return result;
}

This function works pretty well. You are wondering: Wow, is this easy to write? Not yet, please continue below, more “scary” things are waiting.

Level 4: Object contains an object or struct like DateTime

In the above level, we write the function to compare each field. But suppose in the Student class, we contain a DateTime or another class?

public class Student
{
public string FirstName {get; set; }
public string LastName {get; set; }
public DateTime BirthDate {get; set; }
public Teacher Teacher {get; set; }
}

public class Teacher
{
public string Name {get; set; }
public string Subject {get; set; }
}

var student1 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
BirthDate = new DateTime (1992, 12, 5),
Teacher = new Teacher {Name = "Le Minh", Subject = "Math"}
};

var student2 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
BirthDate = new DateTime (1992, 12, 5),
Teacher = new Teacher {Name = "Le Minh", Subject = "Math"}
};

Think for a second. To solve this, we will also compare each field, but if the field is an object, then we will compare it with the written DeepEquals function. A basic recursive algorithm only.

public static bool DeepEquals (this object obj, object another)
{
if (ReferenceEquals (obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType ()! = another.GetType ()) return false;

// If the property is not a class, just int, double, DateTime, etc. v
// Call regular equal function
if (! obj.GetType (). IsClass) return obj.Equals (another);

var result = true;
foreach (var property in obj.GetType (). GetProperties ())
{
var objValue = property.GetValue (obj);
var anotherValue = property.GetValue (another);
// Continue recursion
if (! objValue.DeepEquals (anotherValue)) result = false;
}
return result;
}

Wow, it’s done, all problems are solved, you are complimenting yourself. Oh, what about the case of not an object, but a List, how about this stretch.

Level 5: Compare 2 lists

Fortunately, we can write the extension method for the List as follows (The word <T> is generic, you can read this article to review).

public static bool DeepEquals <T> (this IEnumerable <T> obj, IEnumerable <T> another)
{
if (ReferenceEquals (obj, another)) return true;
if ((obj == null) || (another == null)) return false;

bool result = true;
// Browse each element in 2 given list
using (IEnumerator <T> enumerator1 = obj.GetEnumerator ())
using (IEnumerator <T> enumerator2 = another.GetEnumerator ())
{
while (true)
{
bool hasNext1 = enumerator1.MoveNext ();
bool hasNext2 = enumerator2.MoveNext ();

// If there is 1 list, or 2 different elements, exit the loop
if (hasNext1! = hasNext2 ||! enumerator1.Current.DeepEquals (enumerator2.Current))
{
result = false;
break;
}

// Stop the loop when 2 lists are all
if (! hasNext1) break;
}
}

return result;
}

Whew, I was done temporarily. Probably nothing else, right?

var list1 = new List <Student> {student1, student2};
var list2 = new List <Student> {student1, student2};

list1 == list2; // True

Level 6: A bunch of other things

You suddenly remember that in C #, there are countless things similar to List, such as Dictionary, HashSet, maybe to write to them all. There are a few more trivial cases, such as the Student class will contain a list of the Teacher; the method we write cannot run.

var teacherA = new Teacher {Name = "Le Minh", Subject = "Math"};
var teacherB = new Teacher {Name = "Tai Phu", Subject = "Physics"};

var student1 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
Age = 15,
BirthDate = new DateTime (1992, 12, 5),
Teacher = new List <Teacher> {teacherA, teacherB}
};

var student2 = new Student
{
FirstName = "Pham",
LastName = "Hoang",
Age = 15,
BirthDate = new DateTime (1992, 12, 5),
Teacher = new List <Teacher> {teacherA, teacherB}
};

At this point, I have also given up, the road ahead is quite complicated and challenging: ‘(You can choose one of the two following options:

  1. For classes that are too difficult, write the Equals function yourself, not too much.
  2. Follow the generic function to the end, by continuing to read this article.

Final level: JSON

The problem solving is more straightforward than you think. Let’s serialize those two objects as JSON string, compare the two generated strings. (Serializing out JSON has solved 99% of the complicated problem related to data types, thankfully: D).

Steps to be followed:

  1. Add Reference Newtonsoft.JSON according to the instructions, the result, as shown in figure 3, is ok.

2. Write a simple, compact comparison function:

public static bool JSONEquals (this object obj, object another)
{
if (ReferenceEquals (obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType ()! = another.GetType ()) return false;

var objJson = JsonConvert.SerializeObject (obj);
var anotherJson = JsonConvert.SerializeObject (another);

return objJson == anotherJson;
}

There are a few other libraries that you can search on google with keywords: Deep Compare C #.

Because the article is quite long, slightly inclined to technical, I tried to make it more attractive. Congratulations if you have read through to the end. The reward for you is patient here: The first five commenters in this article have the right to request me to write an article about one aspect of C #, MVC, or javascript that you want to learn. Good luck.

--

--

Beribey

Always be nice to anybody who has access to my toothbrush.