Implementation of IEqualityComparer <T> for comparing arbitrary properties of any class (including anonymous)

I am writing this neat class that implements IEqualityComparer, so that I can just pass it an anonymous type (or virtually any type with properties), and it will automatically compare the types by comparing the values โ€‹โ€‹of the type properties.

public class CompareProperty<T> : IEqualityComparer<T> { private Type type; private PropertyInfo propInfo; private string _fieldName; public string fieldName { get; set; } public CompareProperty(string fieldName) { this.fieldName = fieldName; } public bool Equals<T>(T x, T y) { if (this.type == null) { type = x.GetType(); propInfo = type.GetProperty(fieldName); } object objX = propInfo.GetValue(x, null); object objY = propInfo.GetValue(y, null); return objX.ToString() == objY.ToString(); } } 

I thought it was a small helper function that I could use many times.

To use this, I have to do:

 var t = typeof(CompareProperty<>); var g = t.MakeGenericType(infoType.GetType()); var c = g.GetConstructor(new Type[] {String.Empty.GetType()}); var obj = c.Invoke(new object[] {"somePropertyName"}); 

Fair enough, but what should I do with the obj variable that it returns?

 someEnumerable.Distinct(obj); 

Overloading an individual extension function does not accept this because it does not see the IEqualityComparer type, but it only sees the object.

 someEnumerable.Distinct((t) obj); someEnumerable.Distinct(obj as t); 

This also does not work. Type / namespace not found (red underline).

How can I get this straight?

+4
source share
2 answers

First I will provide a solution for non-anonymous types, and then I will continue to work for anonymous types. Hope this helps you understand what people tried to say in the comments on your question.

My "universal" IEqualityComparer<> as follows:

 public class PropertyComparer<T> : IEqualityComparer<T> { private readonly PropertyInfo propertyToCompare; public PropertyComparer(string propertyName) { propertyToCompare = typeof(T).GetProperty(propertyName); } public bool Equals(T x, T y) { object xValue = propertyToCompare.GetValue(x, null); object yValue = propertyToCompare.GetValue(y, null); return xValue.Equals(yValue); } public int GetHashCode(T obj) { object objValue = propertyToCompare.GetValue(obj, null); return objValue.GetHashCode(); } } 

Suppose we want to use it with a non-anonymous type first, for example Person :

 public class Person { public int Id { get; set; } public string FirstName { get; set; } public string Surname { get; set; } } 

Usage will be quite simple:

 IEnumerable<Person> people = ... ; // some database call here var distinctPeople = people.Distinct(new PropertyComparer<Person>("FirstName")); 

As you can see, to use PropertyComparer<T> , we need to specify the types of instances ( T ) that will be mapped to each other. What can be T when working with anonymous types? Since they are generated at run time, you cannot use the comparator directly when creating your instance, simply because you do not know T at compile time. Instead, you need to use type-inference so that the C # compiler takes T out of context on its own. The immediate way to do this is to write the following extension method:

 public static class LinqExtensions { public static IEnumerable<T> WithDistinctProperty<T>(this IEnumerable<T> source, string propertyName) { return source.Distinct(new PropertyComparer<T>(propertyName)); } } 

Now it will also work with anonymous types:

 var distinctPeople = people .Select(x => new { x.FirstName, x.Surname }) .WithDistinctProperty("FirstName"); 

Suddenly, there is no need to specify the exact type that the request addresses anywhere, since the C # compiler is smart enough to get it out of context (which in this case is provided from the type source in the extension method).

Hope this helps you.

+6
source

Just add your own checks.

 class PropertyComparer<T, TResult> : IEqualityComparer<T> { private Func<T, TResult> _getProperty; public PropertyComparer(Func<T, TResult> predicate) { this._getProperty = predicate; } public bool Equals(T x, T y) { return this._getProperty(x).Equals(_getProperty(y)); } public int GetHashCode(T obj) { return this._getProperty(obj).GetHashCode(); } } 

Class with extension method:

 public static class IEnumerableExtensions { public static IEnumerable<TSource> DistinctBy<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, TResult> predicate) { return source.Distinct(new PropertyComparer<TSource, TResult>(predicate)); } } 

Using:

 someEnumerable.DistinctBy(i => i.SomeProperty); 
0
source

All Articles