private bool CompareMemberValues(ObjectComparisonData <T> data, Type memberType, object val1, object val2)
        {
            bool result = false;

            if (memberType == typeof(string) || memberType.IsValueType)
            {
                result = val1.Equals(val2);
            }
            else if (memberType.IsClass)
            {
                if (val1 != null && val2 != null)
                {
                    // TODO: look up specific implementation for lists, collections, etc.
                    if (memberType.GetInterfaces().Contains(typeof(IEnumerable)))
                    {
                        result = (val1 as IEnumerable).Count() == (val2 as IEnumerable).Count();
                    }
                    else
                    {
                        result = ClassesHaveEqualValues(val1, val2, data, memberType);
                    }
                }
                else
                {
                    result = (val1 == null) && (val2 == null);
                }
            }

            return(result);
        }
 public ObjectComparisonData(ObjectComparisonData <T> data)
 {
     VisitedObjectList1 = new List <long>(data.VisitedObjectList1);
     VisitedObjectList2 = new List <long>(data.VisitedObjectList2);
     ObjectIdGenerator1 = data.ObjectIdGenerator1;
     ObjectIdGenerator2 = data.ObjectIdGenerator2;
     WhereAmI           = WhereAmI;
 }
        private static void CheckForCircularReference(object obj1, object obj2, ObjectComparisonData <T> data)
        {
            var id1 = data.ObjectIdGenerator1.GetId(obj1, out bool firstTime1);
            var id2 = data.ObjectIdGenerator2.GetId(obj2, out bool firstTime2);

            if (data.VisitedObjectList1.Contains(id1) || data.VisitedObjectList2.Contains(id2))
            {
                throw new ArgumentException("Circular reference");
            }
            else
            {
                data.VisitedObjectList1.Add(id1);
                data.VisitedObjectList2.Add(id2);
            }
        }
        private bool ClassesHaveEqualValues(object obj1, object obj2, ObjectComparisonData <T> data, Type memberType)
        {
            CheckForCircularReference(obj1, obj2, data);

            var members      = GetPublicMembers(memberType);
            var enumerator   = members.GetEnumerator();
            var objectsEqual = true;

            while (objectsEqual && enumerator.MoveNext())
            {
                var member       = (MemberInfo)enumerator.Current;
                var propertyType = member.GetUnderlyingType();

                var val1 = member.GetValue(obj1);
                var val2 = member.GetValue(obj2);

                // Append the current member to the current expression to form a new expression.
                var currentPropertyExpression = Expression.PropertyOrField(data.WhereAmI, member.Name);

                // TODO: Collections are not implemented yet.
                // Rules for specific properties take precedence over rules for types.
                if (this._equalityFunctionByExpression.TryGetValue(GetExpressionString(currentPropertyExpression),
                                                                   out Func <object, object, bool> func1))
                {
                    objectsEqual = func1(val1, val2);
                }
                else if (_typeOverrides.TryGetValue(propertyType, out Func <object, object, bool> func2))
                {
                    objectsEqual = func2(val1, val2);
                }
                else
                {
                    var objData = new ObjectComparisonData <T>(data)
                    {
                        WhereAmI = currentPropertyExpression
                    };
                    objectsEqual = CompareMemberValues(objData, propertyType, val1, val2);
                }
            }

            return(objectsEqual);
        }