C# 实施IEqualityComparer<;T>;用于比较任何类(包括匿名类)的任意属性

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

我正在编写这个实现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();
        }
    }
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);