我正在编写实现IEqualityComparer的简洁类,以便我可以将任何匿名类型(或者实际上是具有属性的任何类型)传递给它,并且它将通过比较类型的属性值来自动比较类型。
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();
}
}
我以为这是我可以使用很多次的不错的小辅助函数。
为了使用它,我必须做:
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);
这也不起作用。找不到类型/ namespace (红色下划线)。
我该如何直截了当?
最佳答案
我将首先为非匿名类型提供解决方案,然后再将其扩展为也适用于匿名类型。希望它可以帮助您了解人们在对您的问题的评论中要说的内容。
我的“通用” IEqualityComparer<>
看起来像这样:
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();
}
}
假设我们要首先将其与非匿名类型一起使用,例如
Person
:public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
用法非常简单:
IEnumerable<Person> people = ... ; // some database call here
var distinctPeople = people.Distinct(new PropertyComparer<Person>("FirstName"));
如您所见,要使用
PropertyComparer<T>
,我们需要指定要相互比较的类型(T
)实例。处理匿名类型时,T
是什么?由于它们是在运行时生成的,因此不能直接通过创建比较器的实例来使用比较器,这仅仅是因为您在编译时不知道T
。相反,您需要使用type-inference来让C#编译器自行从上下文推断T
。做到这一点的好方法是编写以下扩展方法:public static class LinqExtensions
{
public static IEnumerable<T> WithDistinctProperty<T>(this IEnumerable<T> source, string propertyName)
{
return source.Distinct(new PropertyComparer<T>(propertyName));
}
}
现在,它也可以使用匿名类型:
var distinctPeople = people
.Select(x => new { x.FirstName, x.Surname })
.WithDistinctProperty("FirstName");
突然之间,无需指定查询要在任何地方处理的确切类型,因为C#编译器足够聪明,可以从上下文中进行推断(在这种情况下,该上下文是从
source
参数的类型中提供的)。扩展方法)。希望这会帮助你。
关于c# - 实现IEqualityComparer <T>以比较任何类的任意属性(包括匿名),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11345854/