简体   繁体   English

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

[英]Comparing Nested object properties using C#

I have a method which compares two objects and returns a list of all the property names which are different. 我有一个比较两个对象的方法,并返回一个不同的所有属性名称的列表。

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

For example if I have two classes as follows: 例如,如果我有两个类如下:

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

I am trying to compare the following two employee instances: 我想比较以下两个员工实例:

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;

So when I pass these two employee objects to my GetDifferingProperties method currently it returns FirstName and EmployeeAddress, but it does not tell me which exact property (which in this case is Address1) in the EmployeeAddress has changed. 因此,当我将这两个雇员对象传递给我的GetDifferingProperties方法时,它返回FirstName和EmployeeAddress,但它并没有告诉我EmployeeAddress中哪个确切的属性(在本例中是Address1)已经改变。 How can I tweak this method to get something like EmployeeAddress.Address1? 我如何调整此方法以获得类似EmployeeAddress.Address1的内容?

It's because you are using != which, for objects, tests the identity of an object rather than its value. 这是因为你正在使用!=对于对象,它测试对象的身份而不是它的值。 The key is to use recursion to generate the list of properties of properties. 关键是使用递归来生成属性的属性列表。 This will go as deep as you want... 这会像你想要的那样深......

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

I can recommend using http://comparenetobjects.codeplex.com/ This has a possibility to compare nested objects, enums, ILists, etc. The project is free and easy to use(Just 1 .cs file). 我可以推荐使用http://comparenetobjects.codeplex.com/这可以比较嵌套对象,枚举,IList等。该项目是免费且易于使用的(仅1 .cs文件)。 Moreover, it is possible to get the values that are different, add properties to ignore, etc. 此外,可以获取不同的值,添加要忽略的属性等。

In principle, you'll need to use the technique you implemented in GetDifferingProperties on the two objects that you want to compare after you get their values (using GetValue in the query). 原则上,您需要在获取其值(在查询中使用GetValue )后,在要比较的两个对象上使用在GetDifferingProperties实现的技术。 Probably the most straightforward implementation is to make the method recursive: 可能最简单的实现是使方法递归:

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

If you want to use this in practice, you'll probably want to keep track of how to get to the property (this code gives you just a list of property names without information about the object that contains them). 如果你想在实践中使用它,你可能想要跟踪如何到达属性(这段代码只给你一个属性名称列表,而没有关于包含它们的对象的信息)。 You can change the last line from select name to select s.Name + "." + name 您可以从select name更改最后一行以select s.Name + "." + name select s.Name + "." + name which will give you a more complete name (eg Address.Name if the property that differs is the Name property of the Address member). select s.Name + "." + name将为您提供更完整的名称(例如,如果不同的属性是Address成员的Name属性,则为Address.Name )。

One point: Your method is not accounting for actual differences in the the EmployeeAddress properties. 一点:您的方法不考虑EmployeeAddress属性的实际差异。 Test it and see. 测试一下,看看。

        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";

The program will still return EmployeeAddress as a non-matching property. 该程序仍将EmployeeAddress作为不匹配的属性返回。 However, if you simply set emp2.EmployeeAddress = emp1Address, you don't get the "non-match." 但是,如果您只是设置emp2.​​EmployeeAddress = emp1Address,则不会出现“不匹配”。

Something something about references... 关于引用的东西......

At any rate, if you want to find what's different about that object, you're going to have to search for what's different about that object. 无论如何,如果你想找到有关该对象的不同之处,你将不得不搜索该对象的不同之处。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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