Radoslav.Ivanov
Radoslav.Ivanov

Reputation: 433

Compare Complex Objects

I need to write a comparer that compares complex objects of various type, I wrote some code but it seems too long to me , so is there any other way of good comparison between two complex objects?

My code:

class CompareObjOfType
{
    private static int _totalFalseCount;

    public static bool CompareObjectsOfType(Type T, object source, object target)
    {
        bool result = false;
        if ((source == null) && (target == null))
        {
            return true;
        }

        if ((source == null) ^ (target == null))
        {
            _totalFalseCount++;
            return false;
        }

        if (T.IsValueType || (T == typeof(string)))
        {
            if (T == typeof(DateTime))
            {
                if (!(((DateTime)source).ToUniversalTime().Equals(((DateTime)target).ToUniversalTime())))
                {
                    _totalFalseCount++;
                    return false;
                }
            }
            else if (T == typeof(string))
            {
                if (string.IsNullOrEmpty((string)source) ^ string.IsNullOrEmpty((string)target))
                {
                    _totalFalseCount++;
                    return false;
                }
                if (!(string.IsNullOrEmpty((string)source) && string.IsNullOrEmpty((string)target)))
                {
                    _totalFalseCount++;
                    return false;
                }

                if (!(((string)source).Equals((string)target)))
                {
                    _totalFalseCount++;
                    return false;
                }
            }
            else
            {
                if (!(source.ToString().Equals(target.ToString())))
                {
                    _totalFalseCount++;
                    return false;
                }
            }
            return true;
        }
        else
        {
            var properties = T.GetProperties();
            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;

                if (propertyType.IsArray || propertyType.IsGenericType)
                {
                    var sourceValue = property.GetValue(source);
                    var targetValue = property.GetValue(target);

                    if ((sourceValue == null) && (targetValue == null))
                    {
                        result = true;
                        continue;
                    }
                    if ((sourceValue == null) ^ (targetValue == null))
                    {
                        _totalFalseCount++;
                        result = false;
                        continue;
                    }

                    var sourceCount = ((IList)sourceValue).Count;
                    var targetCount = ((IList)targetValue).Count;

                    if (sourceCount != targetCount)
                    {
                        _totalFalseCount++;
                        result = false;
                        continue;
                    }

                    for (int i = 0; i < sourceCount; i++)
                    {
                        Type elementType = propertyType.IsArray
                                               ? propertyType.GetElementType()
                                               : propertyType.GetGenericArguments().First();
                        result = CompareObjectsOfType(elementType, ((IList)sourceValue)[i],
                                                      ((IList)targetValue)[i]);
                    }
                }
                else
                {
                    result = CompareObjectsOfType(propertyType, property.GetValue(source), property.GetValue(target));
                }
            }
        }
        return result;
    }
}

Upvotes: 0

Views: 212

Answers (2)

Gary Brunton
Gary Brunton

Reputation: 1950

I use this code a lot in my tests. Its not perfect by no means but might be good enough. Note, the ignore params argument can contain a list of property names that you don't want to compare.

    public static void AssertArePropertiesEqual<T>(T expectedObj, T actualObj, params string[] ignore) where T : class
    {
        if (expectedObj != null && actualObj != null)
        {
            var type = typeof(T);
            if (type.IsPrimitive || type == typeof(string))
            {
                Assert.AreEqual(expectedObj, actualObj);
                return;
            }
            var ignoreList = new List<string>(ignore);
            foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (ignoreList.Contains(pi.Name)) continue;
                var selfValue = type.GetProperty(pi.Name).GetValue(expectedObj, null);
                var toValue = type.GetProperty(pi.Name).GetValue(actualObj, null);
                var selfValueDate = selfValue as DateTime?;
                var toValueDate = toValue as DateTime?;

                if (selfValueDate.HasValue && toValueDate.HasValue)
                {
                    Assert.IsTrue(Math.Abs((selfValueDate.Value - toValueDate.Value).TotalSeconds) < 1,
                                  string.Format("The compare of [{0}] properties failed. Expected Date:{1}  Actual Date: {2}", pi.Name,
                                                selfValueDate, toValueDate));
                }
                else
                {
                    Assert.AreEqual(selfValue, toValue, string.Format("The compare of [{0}] properties failed.", pi.Name));
                }
            }
            return;
        }
        Assert.AreEqual(expectedObj, actualObj);
    }

Upvotes: 1

lightbricko
lightbricko

Reputation: 2709

Implement IComparer. There is an example on msdn and a lot of examples on the world wide web.

All you need to check is if the first object is semantically considered to be less than, equal to or greater than the second object.

You can then easily order your objects in LINQ queries or wherever you need them to be ordered.

However, it might be better to re-think the design of your classes. Such extensive comparison (as in your code) is rarely needed.


If you only need to check for equality (not less than or greater than), then an IEqualityComparer comparer is enough, as commented by Kris.

Upvotes: 0

Related Questions