Introduction

Objects Comparer is an object-to-object comparer that allows you to compare objects recursively member by member and defines custom comparison rules for certain properties, fields, or types.

Objects Comparer can be considered as ready to use a framework or as an idea for similar solutions. This article focuses on using a framework than on implementation. If you are interested in implementation, modification, or you have any ideas on how to make this framework better, please feel free to contact me.

Installation

Objects Comparer Framework can be installed as a NuGet package or downloaded from GitHub.

Install-Package ObjectsComparer

GitHub

Basic Example

public class ClassA {
    public string StringProperty {
        get;
        set;
    }
    public int IntProperty {
        get;
        set;
    }
}
var a1 = new ClassA {
    StringProperty = "String", IntProperty = 1
};
var a2 = new ClassA {
    StringProperty = "String", IntProperty = 1
};
var comparer = new Comparer<ClassA>();
var isEqual = comparer.Compare(a1, a2);
Debug.WriteLine("a1 and a2 are " + (isEqual ? "equal" : "not equal"));
a1 and a2 are equal
var a1 = new ClassA {
    StringProperty = "String", IntProperty = 1
};
var a2 = new ClassA {
    StringProperty = "String", IntProperty = 2
};
var comparer = new Comparer<ClassA>();
IEnumerable<Difference> differenses;
var isEqual = comparer.Compare(a1, a2, out differenses);
var differensesList = differenses.ToList();
Debug.WriteLine("a1 and a2 are " + (isEqual ? "equal" : "not equal"));
if (!isEqual) {
    Debug.WriteLine("Differences:");
    Debug.WriteLine(string.Join(Environment.NewLine, differensesList));
}

a1 and a2 are not equal.

Differences

Comparison Settings

The framework provides some useful comparison settings.

RecursiveComparison is true by default.

EmptyAndNullEnumerablesEqual is false by default.

Comparison Settings class allows storing custom values that can be used in custom comparers.

SetCustomSetting<T>(T value, string key = null)  
GetCustomSetting<T>(string key = null)  

Overriding comparison rules

Comparer should be inherited from AbstractValueComparer<T> or should implement IValueComparer<T> .

public class MyComparer: AbstractValueComparer < string > {  
    public override bool Compare(string obj1, string obj2, ComparisonSettings settings) {  
        return obj1 == obj2; //Implement comparison logic here  
    }  
}  

Type comparison rule override.

comparer.AddComparerOverride<string>(new MyComparer());  

Field comparison rule override could be done in three different ways.

comparer.AddComparerOverride(() => new ClassA().StringProperty, new MyComparer());  
comparer.AddComparerOverride(  
    () => new ClassA().StringProperty, (s1, s2, parentSettings) => s1 == s2, s => s.ToString());  
comparer.AddComparerOverride(  
    () => new ClassA().StringProperty, (s1, s2, parentSettings) => s1 == s2);  

Factory

Factory should implement IComparersFactory or should be inherited from ComparersFactory.

public class MyComparersFactory: ComparersFactory {  
    public override IComparer < T > GetObjectsComparer < T > (ComparisonSettings settings = null, IBaseComparer parentComparer = null) {  
        if (typeof(T) == typeof(ClassA)) {  
            var comparer = new Comparer < ClassA > (settings, parentComparer, this);  
            comparer.AddComparerOverride < Guid > (new MyCustomGuidComparer());  
            return (IComparer < T > ) comparer;  
        }  
        return base.GetObjectsComparer < T > (settings, parentComparer);  
    }  
}  

Non-generic comparer

var comparer = new Comparer<ClassA>();  
var isEqual = comparer.Compare(a1, a2); 

This comparer creates a generic implementation of comparer for each comparison.

Useful value comparers

Some custom comparers can be useful.

DoNotCompareValueComparer. Use it to skip some fields/types. There is a singleton implementation.(DoNotCompareValueComparer.Instance).

DynamicValueComparer<T>. Receives comparison rule as a constructor parameter.

NulableStringsValueComparer. Null and empty strings are equal.

Examples

There are some examples of how Objects Comparer can be used.

NSubstitute is used for developing unit tests.

Example 1. Expected message

Example 2. Person comparison

Differences