递归检查对象属性并按属性比较两个对象属性

时间:2019-07-07 08:43:20

标签: c# reflection

我最近在类上编写了一个简单的方法,该类对所有属性进行DeepCopy并返回新属性。 下面是三个示例类和DeepCopy方法:

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
    }
}

我想编写单元测试,以进行测试:

  1. 将源对象的所有属性复制到第二个对象,并且值相同。
  2. 使用反射,以递归的方式获取属性列表(因为每个对象都可以具有另一个具有属性的类型……),并确保DeepCopy方法复制了所有属性。如果在DeepCopy方法中将新属性添加到Person类并且未复制到新对象,则此测试的原因将失败。

我已经尝试过使用反射来获取所有属性,但是我遇到的问题之一是,它有时卡在一个循环中。

3 个答案:

答案 0 :(得分:0)

对于单元测试,有lib FluentAssetions

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

期望的用户名是“ jonas”,但“ d”(索引0)附近的“ dennis”有所不同。

答案 1 :(得分:0)

正如@Backs所说的,FluentAssertions是您需要的:

  

要断言两个对象相等(通过Object.Equals的实现),请使用

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

答案 2 :(得分:0)

尝试一下:

// 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;
}