实现IEqualityComparer 以比较任何类的任意属性(包括匿名)

我正在编写实现IEqualityComparer的这个整洁的类,因此我可以将任何匿名类型传递给它(或实际上任何具有属性的类型),它将通过比较类型的属性值自动比较类型。

public class CompareProperty : IEqualityComparer { 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 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(); } } 

我认为这是一个很好的小助手function,我可以多次使用。

为了使用它,我必须这样做:

 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"}); 

很公平,但我如何处理它返回的obj变量?

 someEnumerable.Distinct(obj); 

不同扩展函数的重载不接受这个,因为它没有看到IEqualityComparer类型,它当然只看到一个对象。

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

这也行不通。 未找到类型/名称空间(红色下划线)。

我该怎么做到这一点?

我将首先为非匿名类型提供解决方案,然后将其扩展为匿名类型。 希望它能帮助您理解人们在对您的问题的评论中试图说些什么。

我的“通用” IEqualityComparer<>看起来像这样:

 public class PropertyComparer : IEqualityComparer { 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(); } } 

假设我们首先要使用非匿名类型,例如Person

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

用法非常简单:

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

如您所见,要使用PropertyComparer ,我们需要指定将要相互比较的类型( T )实例。 在处理匿名类型时, T会是什么? 由于它们是在运行时生成的,因此您不能通过直接创建其实例来使用比较器,因为您在编译时不知道T 相反,您需要使用类型推断让C#编译器自己从上下文中推断T 这样做的好方法是编写以下扩展方法:

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

现在它也适用于匿名类型:

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

突然之间,没有必要指定查询在任何地方处理的确切类型,因为C#编译器足够聪明,可以从上下文中推断出来(在这种情况下,它是从source参数的类型中提供的)扩展方法)。

希望这会帮助你。

只需添加自己的validation。

 class PropertyComparer : IEqualityComparer { private Func _getProperty; public PropertyComparer(Func 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(); } } 

具有扩展方法的类:

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

用法:

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