使用C#比较嵌套对象属性

时间:2010-03-24 01:58:46

标签: c# reflection

我有一个比较两个对象的方法,并返回一个不同的所有属性名称的列表。

public static IList<string> GetDifferingProperties(object source, object target)
{
    var sourceType = source.GetType();
    var sourceProperties = sourceType.GetProperties();
    var targetType = target.GetType();
    var targetProperties = targetType.GetProperties();

    var properties = (from s in sourceProperties
                      from t in targetProperties
                      where s.Name == t.Name &&
                            s.PropertyType == t.PropertyType &&
                            s.GetValue(source,null) != t.GetValue(target,null)
                      select s.Name).ToList();
    return properties;
}

例如,如果我有两个类如下:

public class Address
    {
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
    }

       public class Employee
        {
            public string FirstName { get; set; }
            public string MiddleName { get; set; }
            public string LastName { get; set; }
            public Address EmployeeAddress { get; set; }
        }

我正在尝试比较以下两个员工实例:

var emp1Address = new Address();
        emp1Address.AddressLine1 = "Microsoft Corporation";
        emp1Address.AddressLine2 = "One Microsoft Way";
        emp1Address.City = "Redmond";
        emp1Address.State = "WA";
        emp1Address.Zip = "98052-6399";

        var emp1 = new Employee();
        emp1.FirstName = "Bill";
        emp1.LastName = "Gates";
        emp1.EmployeeAddress = emp1Address;


        var emp2Address = new Address();
        emp2Address.AddressLine1 = "Gates Foundation";
        emp2Address.AddressLine2 = "One Microsoft Way";
        emp2Address.City = "Redmond";
        emp2Address.State = "WA";
        emp2Address.Zip = "98052-6399";

        var emp2 = new Employee();
        emp2.FirstName = "Melinda";
        emp2.LastName = "Gates";
        emp2.EmployeeAddress = emp2Address;

因此,当我将这两个雇员对象传递给我的GetDifferingProperties方法时,它返回FirstName和EmployeeAddress,但它并没有告诉我EmployeeAddress中哪个确切的属性(在本例中是Address1)已经改变了。我如何调整此方法以获得类似EmployeeAddress.Address1?

的内容

4 个答案:

答案 0 :(得分:3)

这是因为您正在使用!=,对于对象,它会测试对象的身份而不是其值。关键是使用递归来生成属性的属性列表。这会像你想要的那样深......

public static IList<string> GetDifferingProperties(object source, object target)
{
  var sourceType = source.GetType();
  var sourceProperties = sourceType.GetProperties();
  var targetType = target.GetType();
  var targetProperties = targetType.GetProperties();

  var result = new List<string>();

  foreach (var property in
      (from s in sourceProperties
       from t in targetProperties
       where s.Name == t.Name &&
       s.PropertyType == t.PropertyType &&
       !Equals(s.GetValue(source, null), t.GetValue(target, null))
       select new { Source = s, Target = t }))
  {
    // it's up to you to decide how primitive is primitive enough
    if (IsPrimitive(property.Source.PropertyType))
    {
      result.Add(property.Source.Name);
    }
    else
    {
      foreach (var subProperty in GetDifferingProperties(
          property.Source.GetValue(source, null),
          property.Target.GetValue(target, null)))
      {
        result.Add(property.Source.Name + "." + subProperty);
      }
    }
  }

  return result;
}

private static bool IsPrimitive(Type type)
{
  return type == typeof(string) || type == typeof(int);
}

答案 1 :(得分:2)

我可以推荐使用http://comparenetobjects.codeplex.com/ 这有可能比较嵌套对象,枚举,IList等。 该项目免费且易于使用(仅1 .cs文件)。此外,可以获取不同的值,添加要忽略的属性等。

答案 2 :(得分:1)

原则上,您需要在获取其值后使用GetDifferingProperties中要实现的两个对象上的技术(在查询中使用GetValue)。可能最直接的实现是使该方法递归:

public static IEnumerable<string> GetDifferingProperties
    (object source, object target) {

  // Terminate recursion - equal objects don't have any differing properties
  if (source == target) return new List<string>();

  // Compare properties of two objects that are not equal
  var sourceProperties = source.GetType().GetProperties();
  var targetProperties = target.GetType().GetProperties();
  return
    from s in sourceProperties
    from t in targetProperties
    where s.Name == t.Name && s.PropertyType == t.PropertyType 
    let sVal = s.GetValue(source, null)
    let tVal = t.GetValue(target, null)

    // Instead of comparing the objects directly using '==', we run
    // the method recursively. If the two objects are equal, it returns
    // empty list immediately, otherwise it generates multiple properties
    from name in GetDifferingProperties(sVal, tVal)
    select name;
}

如果你想在实践中使用它,你可能想要跟踪如何到达属性(这段代码只给你一个属性名称列表,而没有关于包含它们的对象的信息)。您可以将最后一行从select name更改为select s.Name + "." + name,这将为您提供更完整的名称(例如Address.Name,如果不同的属性是Name属性Address 1}}成员)。

答案 3 :(得分:0)

一点:您的方法没有考虑EmployeeAddress属性的实际差异。测试一下,看看。

        emp2Address.AddressLine1 = emp1Address.AddressLine1;// "Gates Foundation";
        emp2Address.AddressLine2 = emp1Address.AddressLine2;// "One Microsoft Way";
        emp2Address.City = emp1Address.City;// "Redmond";
        emp2Address.State = emp1Address.State;// "WA";
        emp2Address.Zip = emp1Address.Zip;// "98052-6399";

程序仍会将EmployeeAddress作为不匹配的属性返回。但是,如果您只是设置emp2.​​EmployeeAddress = emp1Address,则不会出现“不匹配”。

关于引用的东西......

无论如何,如果您想找到该对象的不同之处,您将不得不搜索以了解该对象的不同之处。