private bool ColumnCountsDifferent(CompareParms parms) { DataTable dataTable1 = parms.Object1 as DataTable; DataTable dataTable2 = parms.Object2 as DataTable; if (dataTable1 == null) throw new ArgumentException("parms.Object1"); if (dataTable2 == null) throw new ArgumentException("parms.Object2"); if (dataTable1.Columns.Count != dataTable2.Columns.Count) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = dataTable1.Columns.Count.ToString(CultureInfo.InvariantCulture), Object2Value = dataTable2.Columns.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Columns.Count", Object1 = new WeakReference(parms.Object1), Object2 = new WeakReference(parms.Object2) }; AddDifference(parms.Result, difference); if (parms.Result.ExceededDifferences) return true; } return false; }
private void CompareItems(CompareParms parms) { int count = 0; //Get enumerators by reflection MethodInfo methodInfo = Cache.GetMethod(parms.Object1Type, "GetEnumerator"); IEnumerator enumerator1 = (IEnumerator) methodInfo.Invoke(parms.Object1, null); IEnumerator enumerator2 = (IEnumerator) methodInfo.Invoke(parms.Object2, null); while (enumerator1.MoveNext() && enumerator2.MoveNext()) { string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, string.Empty, string.Empty, count); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = enumerator1.Current, Object2 = enumerator2.Current, BreadCrumb = currentBreadCrumb }; RootComparer.Compare(childParms); if (parms.Result.ExceededDifferences) return; count++; } }
/// <summary> /// Compares the enumerators and ignores the order /// </summary> public void CompareEnumeratorIgnoreOrder(CompareParms parms, bool countsDifferent) { if (countsDifferent) { CompareOutOfOrder(parms, false); if (!parms.Result.ExceededDifferences) { CompareOutOfOrder(parms, true); } } else { bool comparedInOrder = CompareInOrder(parms); if (!comparedInOrder && !parms.Result.ExceededDifferences) { CompareOutOfOrder(parms, false); if (!parms.Result.ExceededDifferences) { CompareOutOfOrder(parms, true); } } } }
/// <summary> /// Compare two dictionaries /// </summary> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) return; try { parms.Result.AddParent(parms.Object1.GetHashCode()); parms.Result.AddParent(parms.Object2.GetHashCode()); //Objects must be the same length bool countsDifferent = DictionaryCountsDifferent(parms); if (parms.Result.ExceededDifferences) return; if (parms.Config.IgnoreCollectionOrder) { IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer); logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent); } else { CompareEachItem(parms); } } finally { parms.Result.RemoveParent(parms.Object1.GetHashCode()); parms.Result.RemoveParent(parms.Object2.GetHashCode()); } }
/// <summary> /// Compare two hash sets /// </summary> public override void CompareType(CompareParms parms) { try { parms.Result.AddParent(parms.Object1.GetHashCode()); parms.Result.AddParent(parms.Object2.GetHashCode()); Type t1 = parms.Object1.GetType(); parms.Object1Type = t1; bool countsDifferent = HashSetsDifferentCount(parms); if (parms.Result.ExceededDifferences) return; if (parms.Config.IgnoreCollectionOrder) { IgnoreOrderLogic logic = new IgnoreOrderLogic(RootComparer); logic.CompareEnumeratorIgnoreOrder(parms, countsDifferent); } else { CompareItems(parms); } } finally { parms.Result.RemoveParent(parms.Object1.GetHashCode()); parms.Result.RemoveParent(parms.Object2.GetHashCode()); } }
/// <summary> /// Compare two enums /// </summary> public override void CompareType(CompareParms parms) { if (parms.Object1.ToString() != parms.Object2.ToString()) { AddDifference(parms); } }
/// <summary> /// Compare two URIs /// </summary> public override void CompareType(CompareParms parms) { Uri uri1 = parms.Object1 as Uri; Uri uri2 = parms.Object2 as Uri; //This should never happen, null check happens one level up if (uri1 == null || uri2 == null) return; if (uri1.OriginalString != uri2.OriginalString) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = NiceString(uri1.OriginalString), Object2Value = NiceString(uri2.OriginalString), ChildPropertyName = "OriginalString", Object1 = new WeakReference(parms.Object1), Object2 = new WeakReference(parms.Object2) }; AddDifference(parms.Result, difference); } }
private bool ListsHaveDifferentCounts(CompareParms parms) { IList ilist1 = parms.Object1 as IList; IList ilist2 = parms.Object2 as IList; if (ilist1 == null) throw new ArgumentException("parms.Object1"); if (ilist2 == null) throw new ArgumentException("parms.Object2"); //Objects must be the same length if (ilist1.Count != ilist2.Count) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = ilist1.Count.ToString(CultureInfo.InvariantCulture), Object2Value = ilist2.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Count", Object1 = new WeakReference(ilist1), Object2 = new WeakReference(ilist2) }; AddDifference(parms.Result, difference); if (parms.Result.ExceededDifferences) return true; } return false; }
private bool OneOfTheStringsIsNull(CompareParms parms) { if (parms.Object1 == null || parms.Object2 == null) { AddDifference(parms); return true; } return false; }
/// <summary> /// Compare two byte array objects /// </summary> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms == null || parms.Object1 == null || parms.Object2 == null) return; if (ListsHaveDifferentCounts(parms)) return; CompareItems(parms); }
/// <summary> /// Compare the fields of a class /// </summary> public void PerformCompareFields(CompareParms parms) { var currentFields = GetCurrentFields(parms); foreach (FieldInfo item in currentFields) { CompareField(parms, item); if (parms.Result.ExceededDifferences) return; } }
/// <summary> /// Compare the properties of a class /// </summary> public void PerformCompareProperties(CompareParms parms) { IEnumerable<PropertyInfo> currentProperties = GetCurrentProperties(parms); foreach (PropertyInfo info in currentProperties) { CompareProperty(parms, info); if (parms.Result.ExceededDifferences) return; } }
public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) return; DateTime date1 = (DateTime) parms.Object1; DateTime date2 = (DateTime) parms.Object2; if (Math.Abs(date1.Subtract(date2).TotalMilliseconds) > parms.Config.MaxMillisecondsDateDifference) AddDifference(parms); }
/// <summary> /// Compare two objects that implement LINQ Enumerator /// </summary> public override void CompareType(CompareParms parms) { Type t1 = parms.Object1.GetType(); Type t2 = parms.Object2.GetType(); var l1 = TypeHelper.IsEnumerable(t1) ? ConvertEnumerableToList(parms.Object1) : parms.Object1; var l2 = TypeHelper.IsEnumerable(t2) ? ConvertEnumerableToList(parms.Object2) : parms.Object2; parms.Object1 = l1; parms.Object2 = l2; _compareIList.CompareType(parms); }
/// <summary> /// Compare two data sets /// </summary> public override void CompareType(CompareParms parms) { DataSet dataSet1 = parms.Object1 as DataSet; DataSet dataSet2 = parms.Object2 as DataSet; //This should never happen, null check happens one level up if (dataSet1 == null || dataSet2 == null) return; if (TableCountsDifferent(parms, dataSet2, dataSet1)) return; CompareEachTable(parms, dataSet1, dataSet2); }
public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) return; Double double1 = (Double)parms.Object1; Double double2 = (Double)parms.Object2; double difference = Math.Abs(double1 * parms.Config.DoublePrecision); if (Math.Abs(double1 - double2) > difference) AddDifference(parms); }
/// <summary> /// Compare two DateTimeOffset /// </summary> /// <param name="parms"></param> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) return; DateTimeOffset date1 = (DateTimeOffset)parms.Object1; DateTimeOffset date2 = (DateTimeOffset)parms.Object2; if (Math.Abs((date1 - date2).TotalMilliseconds) > parms.Config.MaxMillisecondsDateDifference && (!parms.Config.CompareDateTimeOffsetWithOffsets || date1.Offset == date2.Offset)) AddDifference(parms); }
/// <summary> /// Add a difference for the current parameters /// </summary> /// <param name="parms"></param> protected void AddDifference(CompareParms parms) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = NiceString(parms.Object1), Object2Value = NiceString(parms.Object2), Object1 = new WeakReference(parms.Object1), Object2 = new WeakReference(parms.Object2) }; AddDifference(parms.Result,difference); }
/// <summary> /// Compare two simple types /// </summary> public override void CompareType(CompareParms parms) { //This should never happen, null check happens one level up if (parms.Object1 == null || parms.Object2 == null) return; IComparable valOne = parms.Object1 as IComparable; if (valOne == null) throw new Exception("Expected value does not implement IComparable"); if (valOne.CompareTo(parms.Object2) != 0) { AddDifference(parms); } }
/// <summary> /// Compare two IP End Points /// </summary> public override void CompareType(CompareParms parms) { IPEndPoint ipEndPoint1 = parms.Object1 as IPEndPoint; IPEndPoint ipEndPoint2 = parms.Object2 as IPEndPoint; //Null check happens above if (ipEndPoint1 == null || ipEndPoint2 == null) return; ComparePort(parms, ipEndPoint1, ipEndPoint2); if (parms.Result.ExceededDifferences) return; CompareAddress(parms, ipEndPoint1, ipEndPoint2); }
/// <summary> /// Compare two structs /// </summary> public override void CompareType(CompareParms parms) { if (parms.Result.CurrentStructDepth >= parms.Config.MaxStructDepth) return; try { parms.Result.CurrentStructDepth++; parms.Object1Type = parms.Object1.GetType(); _fieldComparer.PerformCompareFields(parms); _propertyComparer.PerformCompareProperties(parms); } finally { parms.Result.CurrentStructDepth--; } }
private void CompareProp(CompareParms parms, object prop1, object prop2, string propName) { string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, propName); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = prop1, Object2 = prop2, BreadCrumb = currentBreadCrumb }; RootComparer.Compare(childParms); }
/// <summary> /// Compare two pointers /// </summary> public override void CompareType(CompareParms parms) { if ((parms.Object1 is IntPtr && parms.Object2 is IntPtr && ((IntPtr)parms.Object1) != ((IntPtr)parms.Object2)) || (parms.Object1 is UIntPtr && parms.Object2 is UIntPtr && ((UIntPtr)parms.Object1) != ((UIntPtr)parms.Object2))) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1 = new WeakReference(parms.Object1), Object2 = new WeakReference(parms.Object2) }; AddDifference(parms.Result, difference); } }
private void CompareField(CompareParms parms, FieldInfo item) { //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(item.FieldType)) return; //Skip if it should be excluded based on the configuration if (ExcludeLogic.ShouldExcludeMember(parms.Config, item)) return; //If we ignore types then we must get correct FieldInfo object FieldInfo secondFieldInfo = GetSecondFieldInfo(parms, item); //If the field does not exist, and we are ignoring the object types, skip it if (parms.Config.IgnoreObjectTypes && secondFieldInfo == null) return; object objectValue1 = item.GetValue(parms.Object1); object objectValue2 = secondFieldInfo != null ? secondFieldInfo.GetValue(parms.Object2) : null; bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.Parents.ContainsKey(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.Parents.ContainsKey(objectValue2.GetHashCode())); //Skip fields that point to the parent if ((TypeHelper.IsClass(item.FieldType) || TypeHelper.IsInterface(item.FieldType)) && (object1IsParent || object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, item.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }
/// <summary> /// Compare two strings /// </summary> public override void CompareType(CompareParms parms) { if (parms.Config.TreatStringEmptyAndNullTheSame && ((parms.Object1 == null && parms.Object2 != null && parms.Object2.ToString() == string.Empty) || (parms.Object2 == null && parms.Object1 != null && parms.Object1.ToString() == string.Empty))) { return; } if (OneOfTheStringsIsNull(parms)) return; IComparable valOne = parms.Object1 as IComparable; if (valOne != null && valOne.CompareTo(parms.Object2) != 0) { AddDifference(parms); } }
private void CompareEachItem(CompareParms parms) { var enumerator1 = ((IDictionary) parms.Object1).GetEnumerator(); var enumerator2 = ((IDictionary) parms.Object2).GetEnumerator(); while (enumerator1.MoveNext() && enumerator2.MoveNext()) { string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Key"); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = enumerator1.Key, Object2 = enumerator2.Key, BreadCrumb = currentBreadCrumb }; RootComparer.Compare(childParms); if (parms.Result.ExceededDifferences) return; currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, "Value"); childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = enumerator1.Value, Object2 = enumerator2.Value, BreadCrumb = currentBreadCrumb }; RootComparer.Compare(childParms); if (parms.Result.ExceededDifferences) return; } }
/// <summary> /// Compare two timespans /// </summary> public override void CompareType(CompareParms parms) { if (((TimeSpan)parms.Object1).Ticks != ((TimeSpan)parms.Object2).Ticks) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = ((TimeSpan)parms.Object1).Ticks.ToString(CultureInfo.InvariantCulture), Object2Value = ((TimeSpan)parms.Object2).Ticks.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Ticks", Object1 = new WeakReference(parms.Object1), Object2 = new WeakReference(parms.Object2) }; AddDifference(parms.Result, difference); } }
private void CompareAddress(CompareParms parms, IPEndPoint ipEndPoint1, IPEndPoint ipEndPoint2) { if (ipEndPoint1.Address.ToString() != ipEndPoint2.Address.ToString()) { Difference difference = new Difference { ParentObject1 = new WeakReference(parms.ParentObject1), ParentObject2 = new WeakReference(parms.ParentObject2), PropertyName = parms.BreadCrumb, Object1Value = ipEndPoint1.Address.ToString(), Object2Value = ipEndPoint2.Address.ToString(), ChildPropertyName = "Address", Object1 = new WeakReference(ipEndPoint1), Object2 = new WeakReference(ipEndPoint2) }; AddDifference(parms.Result, difference); } }
/// <summary> /// Compare the two fonts /// </summary> public override void CompareType(CompareParms parms) { Font font1 = parms.Object1 as Font; Font font2 = parms.Object2 as Font; if (font1 == null || font2 == null) return; CompareProp(parms, font1.Bold, font2.Bold, "Bold"); CompareProp(parms, font1.FontFamily.Name, font2.FontFamily.Name, "FontFamily.Name"); CompareProp(parms, font1.OriginalFontName, font2.OriginalFontName, "OriginalFontName"); CompareProp(parms, font1.Size, font2.Size, "Size"); CompareProp(parms, font1.SizeInPoints, font2.SizeInPoints, "SizeInPoints"); CompareProp(parms, font1.Strikeout, font2.Strikeout, "Strikeout"); CompareProp(parms, font1.Style, font2.Style, "Style"); CompareProp(parms, font1.SystemFontName, font2.SystemFontName, "SystemFontName"); CompareProp(parms, font1.Underline, font2.Underline, "Underline"); CompareProp(parms, font1.Unit, font2.Unit, "Unit"); }
/// <summary> /// Compare two strings /// </summary> public override void CompareType(CompareParms parms) { if (parms.Config.TreatStringEmptyAndNullTheSame && ((parms.Object1 == null && parms.Object2 != null && parms.Object2.ToString() == string.Empty) || (parms.Object2 == null && parms.Object1 != null && parms.Object1.ToString() == string.Empty))) { return; } if (OneOfTheStringsIsNull(parms)) return; string string1 = parms.Object1 as string; string string2 = parms.Object2 as string; if (string1 != string2) { AddDifference(parms); } }
/// <summary> /// Compare a single property of a class /// </summary> /// <param name="parms"></param> /// <param name="info"></param> /// <param name="object2Properties"></param> private void CompareProperty(CompareParms parms, PropertyEntity info, List <PropertyEntity> object2Properties) { //If we can't read it, skip it if (info.CanRead == false) { return; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(info.PropertyType)) { return; } //Skip if it should be excluded based on the configuration if (info.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, info.PropertyInfo, info.DeclaringType)) { return; } //This is a dynamic property to be excluded on an expando object if (info.IsDynamic && ExcludeLogic.ShouldExcludeDynamicMember(parms.Config, info.Name, info.DeclaringType)) { return; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && info.CanWrite == false) { return; } //If we ignore types then we must get correct PropertyInfo object PropertyEntity secondObjectInfo = GetSecondObjectInfo(info, object2Properties); //If the property does not exist, and we are ignoring the object types, skip it if (parms.Config.IgnoreObjectTypes && secondObjectInfo == null) { return; } //Check if we have custom function to validate property BaseTypeComparer customComparer = null; if (info.PropertyInfo != null) { customComparer = CustomValidationLogic.CustomValidatorForMember(parms.Config, info.PropertyInfo, info.DeclaringType) ?? CustomValidationLogic.CustomValidatorForDynamicMember(parms.Config, info.Name, info.DeclaringType); } object objectValue1; object objectValue2; if (!IsValidIndexer(parms.Config, info, parms.BreadCrumb)) { objectValue1 = info.Value; objectValue2 = secondObjectInfo != null ? secondObjectInfo.Value : null; } else { _indexerComparer.CompareIndexer(parms, info, secondObjectInfo); return; } bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Result.IsParent(objectValue1)); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Result.IsParent(objectValue2)); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(info.PropertyType) || TypeHelper.IsInterface(info.PropertyType) || TypeHelper.IsStruct(info.PropertyType)) && (object1IsParent && object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, info.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb, CustomPropertyComparer = customComparer }; _rootComparer.Compare(childParms); }
private static List <PropertyEntity> GetCurrentProperties(CompareParms parms, object objectValue, Type objectType) { return(HandleDynamicObject(parms.Config, objectValue, objectType) ?? HandleInterfaceMembers(parms, objectValue, objectType) ?? HandleNormalProperties(parms, objectValue, objectType)); }
private static List <PropertyEntity> HandleNormalProperties(CompareParms parms, object objectValue, Type objectType) { IEnumerable <PropertyInfo> properties = Cache.GetPropertyInfo(parms.Result.Config, objectType); return(AddPropertyInfos(parms, objectValue, objectType, properties)); }
/// <summary> /// Compare a single property of a class /// </summary> /// <param name="parms"></param> /// <param name="info"></param> /// <param name="object2Properties"></param> private void CompareProperty(CompareParms parms, PropertyEntity object1Property, PropertyEntity object2Property) { //If we can't read it, skip it if (object1Property?.CanRead == false || object2Property.CanRead == false) { return; } //Skip if this is a shallow compare if (!parms.Config.CompareChildren && TypeHelper.CanHaveChildren(object1Property?.PropertyType) || !parms.Config.CompareChildren && TypeHelper.CanHaveChildren(object2Property.PropertyType)) { return; } //Skip if it should be excluded based on the configuration if (object1Property?.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, object1Property.PropertyInfo) || object2Property.PropertyInfo != null && ExcludeLogic.ShouldExcludeMember(parms.Config, object2Property.PropertyInfo)) { return; } //If we should ignore read only, skip it if (!parms.Config.CompareReadOnly && object1Property?.CanWrite == false || !parms.Config.CompareReadOnly && object2Property.CanWrite == false) { return; } //If the property does not exist, and we are ignoring the object types, skip it //We need to disscus this!!!! //if (parms.Config.IgnoreObjectTypes && secondObjectInfo == null) // return; object objectValue1 = object1Property?.Value; object objectValue2 = object2Property?.Value; /* * //need deep investigation about indexerComparer!!! * if (!IsValidIndexer(parms.Config, object1Property, parms.BreadCrumb)) * { * objectValue1 = object1Property.Value; * objectValue2 = object2Property != null ? object2Property.Value : null; * } * else * { * _indexerComparer.CompareIndexer(parms, object1Property); * return; * } * //need deep investigation about indexerComparer!!! */ bool object1IsParent = objectValue1 != null && (objectValue1 == parms.Object1 || parms.Object1.GetHashCode().Equals(objectValue1.GetHashCode())); bool object2IsParent = objectValue2 != null && (objectValue2 == parms.Object2 || parms.Object2.GetHashCode().Equals(objectValue2.GetHashCode())); //Skip properties where both point to the corresponding parent if ((TypeHelper.IsClass(object1Property?.PropertyType) || TypeHelper.IsInterface(object1Property?.PropertyType) || TypeHelper.IsStruct(object1Property?.PropertyType) || TypeHelper.IsClass(object2Property.PropertyType) || TypeHelper.IsInterface(object2Property.PropertyType) || TypeHelper.IsStruct(object2Property.PropertyType)) && (object1IsParent && object2IsParent)) { return; } string currentBreadCrumb = AddBreadCrumb(parms.Config, parms.BreadCrumb, object1Property.Name); CompareParms childParms = new CompareParms { Result = parms.Result, Config = parms.Config, ParentObject1 = parms.Object1, ParentObject2 = parms.Object2, Object1 = objectValue1, Object2 = objectValue2, BreadCrumb = currentBreadCrumb }; _rootComparer.Compare(childParms); }