简体   繁体   中英

Recursively check the object properties and compare two objects property by property

I have recently written a simple method on a class that does a DeepCopy of all properties and returns the new property. Below are three sample classes and the DeepCopy method:

class Person
{
    public int Age {get; set;}
    public string Name {get; set;}
    Public Address address {get; set;}
    public List<Person> Children {get; set;}
}

class Address
{
    public string StreetAddress {get; set;}
    public int ZipCode {get; set; }
    public Region region {get; set;}    
}

class Region
{
    public string City {get; set;}
    public string Country {get; set;}
}

public static Person DeepCopy(this Person p)
{
    return new Person
    {
        Age = p.Age,
        Name = p.Name,
        Address = p.Address,
        Children = p.Children
    }
}

I want to write unit tests, to test:

  1. All the properties of the source object are copied to the second object and the values are identical.
  2. Using reflection, get the list of the properties recursively (since each object can have a property which is another type which itself has a property...) and make sure the DeepCopy method copies all the properties. The reason for this test is to fail, if a new property is added to the Person class and not copied to the new object in DeepCopy method.

I have already tried using reflection to get all the properties, but one of the problems I have is at some point, it gets stuck in a loop.

For unit tests there is lib FluentAssetions :

string username = "dennis";
username.Should().Be("jonas");

Expected username to be "jonas", but "dennis" differs near 'd' (index 0).

As @Backs said, FluentAssertions is what you need here:

To assert that two objects are equal (through their implementation of Object.Equals), use

string otherObject = "whatever";
theObject.Should().Be(otherObject, "because they have the same values");
theObject.Should().NotBe(otherObject);

Try this:

// Taken from: https://gist.github.com/jonathanconway/3330614
public static bool IsSimpleType(this Type type)
{
    return
      type.IsValueType ||
      type.IsPrimitive ||
      new Type[] {
        typeof(String),
        typeof(Decimal),
        typeof(DateTime),
        typeof(DateTimeOffset),
        typeof(TimeSpan),
        typeof(Guid)
      }.Contains(type) ||
      Convert.GetTypeCode(type) != TypeCode.Object;
}

public static bool CompareIEnumerable(IEnumerable enumerable1, IEnumerable enumerable2)
{
    var obj1Iterator = enumerable1.GetEnumerator();
    var obj2Iterator = enumerable2.GetEnumerator();
    bool has1 = obj1Iterator.MoveNext(), has2 = obj2Iterator.MoveNext();

    //loop through the enumerables
    while (has1 && has2)
    {
        //compare the values deeply
        if (!DeepCompare(obj1Iterator.Current, obj2Iterator.Current))
        {
            return false;
        }

        has1 = obj1Iterator.MoveNext();
        has2 = obj2Iterator.MoveNext();
    }

    // if the loop terminated and has1 != has2, one of them have more items, the are not equal
    return has1 == has2;
}

public static bool DeepEquals<T>(this T obj1, T obj2)
{
    //if one is null and the other is not, they are not equal
    if (obj1 != null ^ obj2 != null)
    {
        return false;
    }
    //else if both are null, they are equal
    else if (obj1 == null && obj2 == null)
    {
        return true;
    }

    Type objectsType = obj1.GetType();

    //if they are a simple type, compare them using .Equals method
    if (objectsType.IsSimpleType())
    {
        return obj1.Equals(obj2);
    }
    //if they are IEnumerable type, compare them using CompareIEnumerable method
    else if (objectsType.GetInterface("IEnumerable") != null)
    {
        return CompareIEnumerable((IEnumerable)obj1, (IEnumerable)obj2);
    }

    //The type is not simple nor IEnumerable, loop through the properties and check if they are equal (Deeply)
    foreach (var member in objectsType.GetMembers().Where(m => m.MemberType.Equals(MemberTypes.Field) || m.MemberType.Equals(MemberTypes.Property)))
    {
        Type t = member.MemberType.Equals(MemberTypes.Field) ?
          ((FieldInfo)member).FieldType :
          ((PropertyInfo)member).PropertyType;
        Func<object, object> getter = member.MemberType.Equals(MemberTypes.Field) ?
          new Func<object, object>(((FieldInfo)member).GetValue) :
          new Func<object, object>(((PropertyInfo)member).GetValue);

        if (!DeepCompare(getter(obj1), getter(obj2)))
        {
            return false;
        }
    }

    return true;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM