[c#] Best way to compare two complex objects

I have two complex objects like Object1 and Object2. They have around 5 levels of child objects.

I need the fastest method to say if they are same or not.

How could this be done in C# 4.0?

This question is related to c# .net

The answer is


Serialize both objects, then calculate Hash Code, then compare.


I found this below function for comparing objects.

 static bool Compare<T>(T Object1, T object2)
 {
      //Get the type of the object
      Type type = typeof(T);

      //return false if any of the object is false
      if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
         return false;

     //Loop through each properties inside class and get values for the property from both the objects and compare
     foreach (System.Reflection.PropertyInfo property in type.GetProperties())
     {
          if (property.Name != "ExtensionData")
          {
              string Object1Value = string.Empty;
              string Object2Value = string.Empty;
              if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
                    Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
              if (type.GetProperty(property.Name).GetValue(object2, null) != null)
                    Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
              if (Object1Value.Trim() != Object2Value.Trim())
              {
                  return false;
              }
          }
     }
     return true;
 }

I am using it and it is working fine for me.


If you don't want to implement IEquatable, you can always use Reflection to compare all the properties: - if they're value type, just compare them -if they are reference type, call the function recursively to compare its "inner" properties.

I'm not thinking about performace, but about simplicity. It depends, however on the exact design of your objects. It could get complicated depending on your objects shape (for example if there are cyclic dependencies between properties). There are, however, several solutions out there that you can use, like this one:

Another option is to serialize the object as text, for example using JSON.NET, and comparing the serialization result. (JSON.NET can handle Cyclic dependencies between properties).

I don't know if by fastest you mean the fastest way to implement it or a code that runs fast. You should not optimize before knowing if you need to. Premature optimization is the root of all evil


Serialize both objects and compare the resulting strings by @JoelFan

So to do this, create a static class like so and use Extensions to extend ALL objects (so you can pass anytype of object, collection, etc into the method)

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;

public static class MySerializer
{
    public static string Serialize(this object obj)
    {
        var serializer = new DataContractJsonSerializer(obj.GetType());
        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, obj);
            return Encoding.Default.GetString(ms.ToArray());
        }
    }
}

Once you reference this static class in any other file, you can do this:

Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();

Now you can simply use .Equals to compare them. I use this for checking if objects are in collections too. It works really well.


You can use extension method, recursion to resolve this problem:

public static bool DeepCompare(this object obj, object another)
{     
  if (ReferenceEquals(obj, another)) return true;
  if ((obj == null) || (another == null)) return false;
  //Compare two object's class, return false if they are difference
  if (obj.GetType() != another.GetType()) return false;

  var result = true;
  //Get all properties of obj
  //And compare each other
  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;
 }

public static bool CompareEx(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;

 //properties: int, double, DateTime, etc, not class
 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);
    //Recursion
    if (!objValue.DeepCompare(anotherValue))   result = false;
 }
 return result;
}

or compare by using Json (if object is very complex) You can use Newtonsoft.Json:

public static bool JsonCompare(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;
}

Serialize both objects and compare the resulting strings


Thanks to the example of Jonathan. I expanded it for all cases (arrays, lists, dictionaries, primitive types).

This is a comparison without serialization and does not require the implementation of any interfaces for compared objects.

        /// <summary>Returns description of difference or empty value if equal</summary>
        public static string Compare(object obj1, object obj2, string path = "")
        {
            string path1 = string.IsNullOrEmpty(path) ? "" : path + ": ";
            if (obj1 == null && obj2 != null)
                return path1 + "null != not null";
            else if (obj2 == null && obj1 != null)
                return path1 + "not null != null";
            else if (obj1 == null && obj2 == null)
                return null;

            if (!obj1.GetType().Equals(obj2.GetType()))
                return "different types: " + obj1.GetType() + " and " + obj2.GetType();

            Type type = obj1.GetType();
            if (path == "")
                path = type.Name;

            if (type.IsPrimitive || typeof(string).Equals(type))
            {
                if (!obj1.Equals(obj2))
                    return path1 + "'" + obj1 + "' != '" + obj2 + "'";
                return null;
            }
            if (type.IsArray)
            {
                Array first = obj1 as Array;
                Array second = obj2 as Array;
                if (first.Length != second.Length)
                    return path1 + "array size differs (" + first.Length + " vs " + second.Length + ")";

                var en = first.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    string res = Compare(en.Current, second.GetValue(i), path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
            {
                System.Collections.IEnumerable first = obj1 as System.Collections.IEnumerable;
                System.Collections.IEnumerable second = obj2 as System.Collections.IEnumerable;

                var en = first.GetEnumerator();
                var en2 = second.GetEnumerator();
                int i = 0;
                while (en.MoveNext())
                {
                    if (!en2.MoveNext())
                        return path + ": enumerable size differs";

                    string res = Compare(en.Current, en2.Current, path);
                    if (res != null)
                        return res + " (Index " + i + ")";
                    i++;
                }
            }
            else
            {
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    try
                    {
                        var val = pi.GetValue(obj1);
                        var tval = pi.GetValue(obj2);
                        if (path.EndsWith("." + pi.Name))
                            return null;
                        var pathNew = (path.Length == 0 ? "" : path + ".") + pi.Name;
                        string res = Compare(val, tval, pathNew);
                        if (res != null)
                            return res;
                    }
                    catch (TargetParameterCountException)
                    {
                        //index property
                    }
                }
                foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
                {
                    var val = fi.GetValue(obj1);
                    var tval = fi.GetValue(obj2);
                    if (path.EndsWith("." + fi.Name))
                        return null;
                    var pathNew = (path.Length == 0 ? "" : path + ".") + fi.Name;
                    string res = Compare(val, tval, pathNew);
                    if (res != null)
                        return res;
                }
            }
            return null;
        }

For easy copying of the code created repository


public class GetObjectsComparison
{
    public object FirstObject, SecondObject;
    public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
}
public struct SetObjectsComparison
{
    public FieldInfo SecondObjectFieldInfo;
    public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue;
    public bool ErrorFound;
    public GetObjectsComparison GetObjectsComparison;
}
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison)
{
    GetObjectsComparison FunctionGet = GetObjectsComparison;
    SetObjectsComparison FunctionSet = new SetObjectsComparison();
    if (FunctionSet.ErrorFound==false)
        foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions))
        {
            FunctionSet.SecondObjectFieldInfo =
            FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions);

            FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject);
            FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject);
            if (FirstObjectFieldInfo.FieldType.IsNested)
            {
                FunctionSet.GetObjectsComparison =
                new GetObjectsComparison()
                {
                    FirstObject = FunctionSet.FirstObjectFieldInfoValue
                    ,
                    SecondObject = FunctionSet.SecondObjectFieldInfoValue
                };

                if (!ObjectsComparison(FunctionSet.GetObjectsComparison))
                {
                    FunctionSet.ErrorFound = true;
                    break;
                }
            }
            else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue)
            {
                FunctionSet.ErrorFound = true;
                break;
            }
        }
    return !FunctionSet.ErrorFound;
}

Based off a few answers already given here I decided to mostly back JoelFan's answer. I love extension methods and these have been working great for me when none of the other solutions would using them to compare my complex classes.

Extension Methods

using System.IO;
using System.Xml.Serialization;

static class ObjectHelpers
{
    public static string SerializeObject<T>(this T toSerialize)
    {
        XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

        using (StringWriter textWriter = new StringWriter())
        {
            xmlSerializer.Serialize(textWriter, toSerialize);
            return textWriter.ToString();
        }
    }

    public static bool EqualTo(this object obj, object toCompare)
    {
        if (obj.SerializeObject() == toCompare.SerializeObject())
            return true;
        else
            return false;
    }

    public static bool IsBlank<T>(this T obj) where T: new()
    {
        T blank = new T();
        T newObj = ((T)obj);

        if (newObj.SerializeObject() == blank.SerializeObject())
            return true;
        else
            return false;
    }

}

Usage Examples

if (record.IsBlank())
    throw new Exception("Record found is blank.");

if (record.EqualTo(new record()))
    throw new Exception("Record found is blank.");

You can now use json.net. Just go on Nuget and install it.

And you can do something like this:

    public bool Equals(SamplesItem sampleToCompare)
    {
        string myself = JsonConvert.SerializeObject(this);
        string other = JsonConvert.SerializeObject(sampleToCompare);

        return myself == other;
    }

You could perhaps make a extension method for object if you wanted to get fancier. Please note this only compares the public properties. And if you wanted to ignore a public property when you do the comparison you could use the [JsonIgnore] attribute.


Use IEquatable<T> Interface which has a method Equals.


I would say that:

Object1.Equals(Object2)

would be what you're looking for. That's if you're looking to see if the objects are the same, which is what you seem to be asking.

If you want to check to see if all the child objects are the same, run them through a loop with the Equals() method.


I'll assume you are not referring to literally the same objects

Object1 == Object2

You might be thinking about doing a memory comparison between the two

memcmp(Object1, Object2, sizeof(Object.GetType())

But that's not even real code in c# :). Because all of your data is probably created on the heap, the memory is not contiguous and you can't just compare the equality of two objects in an agnostic manner. You're going to have to compare each value, one at a time, in a custom way.

Consider adding the IEquatable<T> interface to your class, and define a custom Equals method for your type. Then, in that method, manual test each value. Add IEquatable<T> again on enclosed types if you can and repeat the process.

class Foo : IEquatable<Foo>
{
  public bool Equals(Foo other)
  {
    /* check all the values */
    return false;
  }
}

One way to do this would be to override Equals() on each type involved. For example, your top level object would override Equals() to call the Equals() method of all 5 child objects. Those objects should all override Equals() as well, assuming they are custom objects, and so on until the entire hierarchy could be compared by just performing an equality check on the top level objects.