Checking for Property Differences in Object
I recently had to come up with a way to check the properties to 2 similar objects and find any differences between them. The solution that I came up with was to write a generic extension method that compares the 2 objects and returns a list of ObjectDifferences objects. The ObjectDifferences class is as follows:
public class ObjectDifferences
{
public string FieldName { get; private set; }
public string OriginalVal { get; private set; }
public string NewVal { get; private set; }
public ObjectDifferences(string fieldName, string originalVal, string newVal)
{
FieldName = fieldName;
OriginalVal = originalVal;
NewVal = newVal;
}
}
The Method
Below is the extension method that I ended up using for finding the difference between 2 objects. It takes in the object to compare to and a list of any fields not to compare. For example if you were comparing an object with a unique ID field, you might not want to compare this as it would always be different.
using System;
using System.Collections.Generic;
using System.Reflection;
public static IList<ObjectDifferences> GetDifferences<T>(this T originalValuesObj, T newValuesObj, params string[] ignore)
where T: class
{
if (originalValuesObj == null || newValuesObj == null)
{
throw new ArgumentNullException(originalValuesObj == null ? "Original" : "New");
}
List<string> ignoreList = new List<string>(ignore ?? new string[] { });
IList<ObjectDifferences> differences = new List<ObjectDifferences>();
Type type = typeof(T);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (ignoreList.Contains(pi.Name))
continue;
object oVal = pi.GetValue(originalValuesObj, null);
object nVal = pi.GetValue(newValuesObj, null);
if (!object.Equals(oVal, nVal))
differences.Add(new ObjectDifferences(pi.Name, oVal.ToString(), nVal.ToString()));
}
return differences;
}
Method Use
You can see the use of this method using the following example, using Person class with 4 properties (Forename, Surname, Age and Country).
Person p = new Person("Forename", "Surname", 19, "UK");
Person p2 = new Person("Forename", "Surname", 22, "USA");
List<ObjectDifferences> diff1 = p.GetDifferences(p2);
List<ObjectDifferences> diff2 = p.GetDifferences(p2, "Age", "Country");
In the above example, diff1 will contain 2 ObjectDifferences objects, one for Age and one for Country. diff2 will be empty because we are using the second parameter to specify that we want to ignore the “Age” and “Country” parameters.
Key Points
-
Type type = typeof(T);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
First we get the Type of the objects to be compared and enumerate through every Public and Instance property (specified using BindingFlags enum) in that type, getting a PropertyInfo object for each property. -
if (ignoreList.Contains(pi.Name) continue;
As we enter the loop we check if the current property was specified in the ignore list, if it is continue to the next property. -
```csharp object oVal = pi.GetValue(originalValuesObj, null); object nVal = pi.GetValue(newValuesObj, null);
if (!object.Equals(oVal, nVal)) differences.Add(new ObjectDifferences(pi.Name, oVal.ToString(), nVal.ToString())); ``` Lastly we get an object representing the value for the current property in the original and comparison object. We then compare these 2 objects using Object.Equals(Object, Object). If the objects are different we add a new ObjectDifferences object to the differences object, which is then returned to the calling method.
Limitations
There are a few limitations with this method. In my current project I use the above method on simple DTO objects with no complex types. If this was used, for example, on a LINQ2SQL model with a property that references another table then there might be issues, depending on how they check equality.