Comparing Object Properties in C#

Comparing object properties in c#

I was looking for a snippet of code that would do something similar to help with writing unit test. Here is what I ended up using.

public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class 
{
if (self != null && to != null)
{
Type type = typeof(T);
List<string> ignoreList = new List<string>(ignore);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
if (!ignoreList.Contains(pi.Name))
{
object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
object toValue = type.GetProperty(pi.Name).GetValue(to, null);

if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
{
return false;
}
}
}
return true;
}
return self == to;
}

EDIT:

Same code as above but uses LINQ and Extension methods :

public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self != null && to != null)
{
var type = typeof(T);
var ignoreList = new List<string>(ignore);
var unequalProperties =
from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
where !ignoreList.Contains(pi.Name) && pi.GetUnderlyingType().IsSimpleType() && pi.GetIndexParameters().Length == 0
let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
let toValue = type.GetProperty(pi.Name).GetValue(to, null)
where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
select selfValue;
return !unequalProperties.Any();
}
return self == to;
}

public static class TypeExtensions
{
/// <summary>
/// Determine whether a type is simple (String, Decimal, DateTime, etc)
/// or complex (i.e. custom class with public properties and methods).
/// </summary>
/// <see cref="http://stackoverflow.com/questions/2442534/how-to-test-if-type-is-primitive"/>
public static bool IsSimpleType(
this Type type)
{
return
type.IsValueType ||
type.IsPrimitive ||
new[]
{
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
(Convert.GetTypeCode(type) != TypeCode.Object);
}

public static Type GetUnderlyingType(this MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Event:
return ((EventInfo)member).EventHandlerType;
case MemberTypes.Field:
return ((FieldInfo)member).FieldType;
case MemberTypes.Method:
return ((MethodInfo)member).ReturnType;
case MemberTypes.Property:
return ((PropertyInfo)member).PropertyType;
default:
throw new ArgumentException
(
"Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
);
}
}
}

Compare two objects for properties with different values

I improved a little on Krishnas answer:

public List<string> GetChangedProperties<T>(object A, object B)
{
if (A != null && B != null)
{
var type = typeof(T);
var allProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var allSimpleProperties = allProperties.Where(pi => pi.PropertyType.IsSimpleType());
var unequalProperties =
from pi in allSimpleProperties
let AValue = type.GetProperty(pi.Name).GetValue(A, null)
let BValue = type.GetProperty(pi.Name).GetValue(B, null)
where AValue != BValue && (AValue == null || !AValue.Equals(BValue))
select pi.Name;
return unequalProperties.ToList();
}
else
{
throw new ArgumentNullException("You need to provide 2 non-null objects");
}
}

because it wasn't working for me. This one does and the only other thing you need to make it work is the IsSimpleType()-Extension Method that I adapted from this answer here (I only converted it into an extension method).

public static bool IsSimpleType(this Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// nullable type, check if the nested type is simple.
return type.GetGenericArguments()[0].IsSimpleType();
}
return type.IsPrimitive
|| type.IsEnum
|| type.Equals(typeof(string))
|| type.Equals(typeof(decimal));
}

How to compare the same properties of two different class objects in C#?

I like a more controlled way better where you just type the compaire properties as you did in your sample, perhaps use an icomparable interface.

he reflection option that is offered and will be offered is slow, could give null pointer exceptions etc but write once work always, it's not a nuget package but here you go.

public static List<PropertyInfo> GetDifferences(object test1, object test2)
{
if (test1 is null)
throw new ArgumentNullException(nameof(test1));
if (test2 is null)
throw new ArgumentNullException(nameof(test2));

List<PropertyInfo> differences = new List<PropertyInfo>();
foreach (PropertyInfo property in test1.GetType().GetProperties())
{
if (test2.GetType().GetProperties().Any(a => a.Name.Equals(property.Name, StringComparison.Ordinal)))
{
object value1 = property.GetValue(test1, null);
object value2 = property.GetValue(test2, null);
if ((value1 == null) || !value1.Equals(value2))
{
differences.Add(property);
}
}
}
return differences;
}

It will return the properties that both have and are not the same.

How to compare properties between two objects

If you want to stick with comparison via reflection you should not use != (reference equality which will fail most of comparisons for boxed results of GetProperty calls) but instead use static Object.Equals method.

Sample how to use Equals method to compare two object in your reflection code.

 if (!Object.Equals(
item.GetValue(person, null),
dto.GetType().GetProperty(item.Name).GetValue(dto, null)))
{
diffProperties.Add(item);
}

Comparing Object properties using reflection

You don't necessarily need reflection to perform the comparison. You can write a comparer class that takes two instances of Employee or Address, and compares each field that should match. For any that don't match, you can add a string (or PropertyInfo) element to some list to return to the caller.

Whether you return a PropertyInfo, MemberInfo, or just a string depends on what the caller needs to do with the result. If you actually need to visit the fields that contain differences, the PropertyInfo/MemberInfo may be better - but to just report the differences a string is probaby sufficient.

The main value of reflection would be to write a general purpose object comparer that could take two instances of any kind of object and compare their public fields and properties. This helps avoid writing repetetive comparison code over and over - but that doesn't seem like the case you're in.

How to efficiently compare two objects and count the number of equal properties?

If performance is really important, I think you need Intersect since it uses HashSets.

private static int CompareProps(MyObject a, MyObject b)
{
var aValues = a.GetType().GetProperties().Select(x => x.GetValue(a, null));
var bValues = b.GetType().GetProperties().Select(x => x.GetValue(b, null));

return aValues.Intersect(bValues).Count();
}

And here's the sample usage..

var a = new MyObject
{
prop1 = "abc", // same value
prop2 = "def",
prop3 = 123,
prop4 = 456 // same value
};

var b = new MyObject
{
prop1 = "abc", // same value
prop2 = "jkl",
prop3 = 789,
prop4 = 456 // same value
};

Console.WriteLine(CompareProps(a, b)); // output 2

EDIT:

Tested my solution by running 300X300 list loop.

private static void Run()
{
var alist = new List<MyObject>();
for (var i = 0; i < 300; i++)
{
alist.Add(new MyObject
{
prop1 = "abc",
prop2 = RandomString(),
prop3 = random.Next(),
prop4 = 123
});
}

var blist = new List<MyObject>();
for (var i = 0; i < 300; i++)
{
blist.Add(new MyObject
{
prop1 = "abc",
prop2 = RandomString(),
prop3 = random.Next(),
prop4 = 123
});
}

var watch = new Stopwatch();
watch.Start();

Parallel.For(0, alist.Count, i =>
{
for (var j = 0; j < blist.Count; j++)
{
Console.WriteLine("Result: " + CompareProps(alist[i], blist[j]));
}
});

Console.WriteLine(watch.Elapsed.TotalSeconds + " seconds..");
}

Result: 9.1703053 seconds..

Sample Image

Compare property values with Reflection

Use

if(!object.Equals(one, two)) { ... }

for equality comparison instead. That will invoke the default equality comparator or the overridden one for those types that have implemented it (which includes all value types like int on your example).

!= uses reference equality and therefore produces the same value when the referenced types are exactly the same instance. Using != will give you unexpected results due to the fact you are using GetValue which returns an object.

public object GetValue(object obj)

because one and two are actually reference types (i.e. they are boxed ints instead of value types) the "==" comparator performs a reference equality check.



Related Topics



Leave a reply



Submit