Comparing properties of nested objects using C #

I have a method that compares two objects and returns a list of all property names that 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:

 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 instances of employees:

 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 the GetDifferingProperties method, it currently returns FirstName and EmployeeAddress, but it does not tell me which exact property (which in this case is Address1) in EmployeeAddress has changed. How can I configure this method to get something like EmployeeAddress.Address1?

+4
source share
4 answers

This is because you use != , Which for objects checks the identifier of the object, not its value. The key is to use recursion to create a list of property properties. It will be 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 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); } 
+3
source

I can recommend using http://comparenetobjects.codeplex.com/ It has the ability to compare nested objects, enumerations, IList, etc. The project is free and easy to use (only 1 .cs file). In addition, you can get values ​​that are different, add properties to ignore, etc.

+2
source

Basically, you will need to use the technique implemented in GetDifferingProperties for the two objects you want to compare after getting their values ​​(using GetValue in the request). Probably the easiest 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 probably want to keep track of how to get to the property (this code gives you 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 s.Name + "." + name , which will give you a more complete name (for example, Address.Name if the property that is different is the Name property of the Address member).

+1
source

One point: your method does not take into account the actual differences in the properties of EmployeeAddress. Check it out 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 mismatch property. However, if you just set emp2.EmployeeAddress = emp1Address, you will not get a "mismatch".

Something about links ...

Anyway, if you want to find something else in this object, you will have to look for something that is different from this object.

0
source

Source: https://habr.com/ru/post/1304952/


All Articles