using static System.Console;
public static void Main()
Complex = new ComplexType { FieldF = "diff" }
var result = m1.Difference(m2);
public static class ReflectionExtensions
public static TModel Difference<TModel>(this TModel model1, TModel model2) where TModel : class, new() =>
Difference(typeof(TModel), model1, model2);
private static TModel Difference<TModel>(Type type, TModel model1, TModel model2) where TModel : class, new()
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
TModel result = type.GetConstructor(new Type[] { }).Invoke(null) as TModel;
result = properties.Aggregate(result, (seed, property) =>
var value1 = model1 is null ? null : property.GetValue(model1);
var value2 = model2 is null ? null : property.GetValue(model2);
if (!AreEqual(value1, value2))
var value = IsDefault(value1) ? value2 : value1;
property.SetValue(seed, value);
private static bool IsDefault<TValue>(TValue value) =>
(value?.Equals(default(TValue))) ?? true;
private static bool AreEqual(object value1, object value2)
if (value1 is null && value2 is null) return true;
if (value1 is null && value2 is not null) return false;
if (value1 is not null && value2 is null) return false;
if (value1 is string str1 && value2 is string str2)
Type type = value1.GetType();
: AreEqualClass(type, value1, value2);
private static bool AreEqualClass<TValue>(Type type, TValue value1, TValue value2) where TValue : class, new()
TValue difference = Difference(type, value1, value2);
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
return properties.All(property =>
var value = property.GetValue(difference);
public string FieldF { get; set; }
public override string ToString()
return $"{{ F = {FieldF} }}";
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
public int FieldD { get; set; }
public string FieldE { get; set; }
public ComplexType Complex { get; set; }
public override string ToString()
return $"{{ A = {FieldA}, B = {FieldB}, C = {FieldC}, D = {FieldD}, E = {FieldE}, Complex = {Complex}";