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?
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.
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);