C# 实施IEqualityComparer<;T>;用于比较任何类(包括匿名类)的任意属性
我正在编写这个实现IEqualityComparer的整洁类,这样我就可以将任何匿名类型(或者实际上是任何具有属性的类型)传递给它,它将通过比较类型的属性值来自动比较类型C# 实施IEqualityComparer<;T>;用于比较任何类(包括匿名类)的任意属性,c#,generics,reflection,equality,iequalitycomparer,C#,Generics,Reflection,Equality,Iequalitycomparer,我正在编写这个实现IEqualityComparer的整洁类,这样我就可以将任何匿名类型(或者实际上是任何具有属性的类型)传递给它,它将通过比较类型的属性值来自动比较类型 public class CompareProperty<T> : IEqualityComparer<T> { private Type type; private PropertyInfo propInfo; private string _f
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();
}
}
distinct扩展函数的重载不接受这一点,因为它看不到IEqualityComparer类型,当然,它只看到一个对象
someEnumerable.Distinct((t) obj);
someEnumerable.Distinct(obj as t);
这也不行。找不到类型/命名空间(红色下划线)
我将首先为非匿名类型提供一个解决方案,然后将其扩展到匿名类型。希望它能帮助你理解人们在评论你的问题时想说些什么 我的“通用”
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();
}
}
使用方法非常简单:
IEnumerable<Person> people = ... ; // some database call here
var distinctPeople = people.Distinct(new PropertyComparer<Person>("FirstName"));
现在它还可以处理匿名类型:
var distinctPeople = people
.Select(x => new { x.FirstName, x.Surname })
.WithDistinctProperty("FirstName");
突然之间,不需要指定查询在任何地方处理的确切类型,因为C#编译器足够聪明,可以从上下文(在本例中,上下文是从扩展方法中的source
参数的类型提供的)中推断出来
希望这对你有帮助 只需添加自己的验证
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();
}
}
仅供参考,匿名类型的默认比较器已基于属性值进行比较。
someEnumerable
的类型是什么?在不知道T
的情况下,很难看到如何编译它,此时反射的一半会消失……我认为尝试一般性地实现它是不明智的。你在添加什么?someEnumerable是IEnumerable类型的。infoType是一个简单的匿名类型,具有一些属性。Hm匿名类型的默认比较器是什么意思?它叫什么?返回objX.ToString()==objY.ToString()代码>不智能。为所有基元创建一个相等函数,然后递归地进入类型,直到到达基元。这是一个通用的解决方案,但由于反射,速度较慢,并且具有硬编码的属性名称(“FirstName”)。我不认为这会在生产环境中运行。@Tymek“Slow”是一个相对的术语。如果您有另一个更高效的解决方案,并且仍然满足OP的需求,我将非常有兴趣看到它。关于硬编码属性名称-这可以通过使用类似的方法解决,但我不想使问题进一步复杂化。让UniversalEqualityAdapter:IEqualityComparer
的构造函数使用Func
和可选的IEqualityComparer
(或者使用EqualityComparer.Default
?然后,要制作一个测试同一X坐标的点
实例的EqualityComparer,可以创建新的UniversalEqualityComparer((点pt)=>pt.X)
。这应该可以避免反射开销,同时也更加通用。@supercat是的,但是对于每个T
,您必须手动指定一个比较Func
。当然,这比创建一个全新的IEqualityComparer
实现要好得多,但它仍然不是自动的。@NikolaAnusev:它不是一个比较函数;它是一个将对象转换为其他类似类型的函数。从实例中提取属性值所需的函数比用引号括起来的函数名稍长,但不会太长。
IEnumerable<Person> people = ... ; // some database call here
var distinctPeople = people.Distinct(new PropertyComparer<Person>("FirstName"));
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");
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();
}
}
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));
}
}
someEnumerable.DistinctBy(i => i.SomeProperty);