private ComparisonRule GetComparisonRuleOrNull(Type type1, Type type2, string type1PropertyName, string type2PropertyName)
        {
            ComparisonRule comparisonRule =
                this.comparisonRules?.SingleOrDefault(rule =>
                                                      rule.Type1 == type1 &&
                                                      rule.Type2 == type2 &&
                                                      rule.Type1PropertyName == type1PropertyName &&
                                                      rule.Type2PropertyName == type2PropertyName) ??
                this.comparisonRules?.SingleOrDefault(rule =>
                                                      rule.Type2 == type1 &&
                                                      rule.Type1 == type2 &&
                                                      rule.Type2PropertyName == type1PropertyName &&
                                                      rule.Type1PropertyName == type2PropertyName)?.Flip();

            return(comparisonRule);
        }
        /// <summary>
        /// Compares the two types, ensuring that all properties of <paramref name="o2"/> are matched by corresponding properties of <paramref name="o1"/>.
        /// </summary>
        private CheckEqualityResult DoesComplexTypeOneMatchTypeTwo(object o1, object o2)
        {
            Type o1Type = o1.GetType();
            Type o2Type = o2.GetType();

            PropertyInfo[] o1Properties = o1Type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            PropertyInfo[] o2Properties = o2Type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

            List <string> o1PropertyNames = o1Properties.Select(prop => prop.Name).ToList();

            foreach (PropertyInfo o2Property in o2Properties)
            {
                object o2Value        = o2Property.GetValue(o2);
                string o1PropertyName = o2Property.Name;

                //Find the corresponding o1 property if there is one
                if (!o1PropertyNames.Contains(o2Property.Name))
                {
                    string mappedPropertyName = this.GetPropertyMappingOrNull(o2Type, o2Property.Name);
                    if (!string.IsNullOrEmpty(mappedPropertyName))
                    {
                        //Property names don't match but we have a mapping which maps them
                        o1PropertyName = mappedPropertyName;
                    }
                    else
                    {
                        return(CheckEqualityResult.False(string.Format("Unknown property: {0} on type {1}", o2Property.Name, o2Type)));
                    }
                }

                PropertyInfo o1Property = o1Type.GetProperty(o1PropertyName);

                try
                {
                    object o1Value = o1Property.GetValue(o1);

                    ComparisonRule customComparisonRule = this.GetComparisonRuleOrNull(o1Type, o2Type, o1PropertyName, o2Property.Name);

                    if (customComparisonRule != null)
                    {
                        CheckEqualityResult result = customComparisonRule.Comparer(o1Value, o2Value);
                        if (!result.Equal)
                        {
                            return(result);
                        }
                    }
                    else
                    {
                        CheckEqualityResult result = this.CheckEquality(o1Value, o2Value);
                        if (!result.Equal)
                        {
                            return(result);
                        }
                    }
                }
                catch (TargetInvocationException e)
                {
                    if (this.shouldSwallowPropertyReadException(e))
                    {
                        throw;
                    }
                }
            }

            return(CheckEqualityResult.True);
        }