简体   繁体   English

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

[英]Implementing IEqualityComparer<T> for comparing arbitrary properties of any class (including anonymous)

I am writing this neat class implementing IEqualityComparer, so that I can just pass any anonymous type to it (or actually any type with properties) and it will automatically compare the types by comparing the property values of the types. 我正在编写实现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();
        }
    }

I thought this was a nice little helper function I could use many times. 我认为这是一个很好的小助手功能,我可以多次使用。

In order 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 do I do with the obj variable it returns? 很公平,但我如何处理它返回的obj变量?

someEnumerable.Distinct(obj);

The overload of the distinct extension function does not accept this, because it does not see a IEqualityComparer type, it only sees an object, of course. 不同扩展函数的重载不接受这个,因为它没有看到IEqualityComparer类型,它当然只看到一个对象。

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

This also doesn't work. 这也行不通。 Type/Namespace not found (red underline). 未找到类型/名称空间(红色下划线)。

How do I get this straight? 我如何直截了当?

I'll first provide a solution for non-anonymous types and afterwards extend it to work for anonymous types as well. 我将首先为非匿名类型提供解决方案,然后将其扩展为匿名类型。 Hopefully, it will help you to understand what people were trying to say in comments to your question. 希望它能帮助您理解人们在评论中对您的问题所说的内容。

My "universal" IEqualityComparer<> looks like this: 我的“通用” 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();
    }
}

Say that we want to use it with non-anonymous type first, like Person : 假设我们首先要使用非匿名类型,例如Person

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

The usage would be quite straightforward: 用法非常简单:

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

As you can see, to use the PropertyComparer<T> , we need to specify the type (the T ) instances of which are going to be compared against each other. 如您所见,要使用PropertyComparer<T> ,我们需要指定要相互比较的类型( T )实例。 What would the T be when dealing with anonymous types? 在处理匿名类型时, T会是什么? Since they are generated at runtime, you cannot use the comparer by directly creating its instance, simply because you do not know the T at compile time. 由于它们是在运行时生成的,因此您无法通过直接创建其实例来使用比较器,因为您在编译时不知道T Instead, you need to use type-inference to let the C# compiler infer T from context on its own. 相反,您需要使用类型推断让C#编译器自己从上下文中推断T Neat 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");

All of a sudden, there is no need to specify the exact type the query is dealing with anywhere, since C# compiler is smart enough to infer it from the context (which, in this case, is being provided from the type of source parameter in the extension method). 突然之间,没有必要指定查询在任何地方处理的确切类型,因为C#编译器足够聪明,可以从上下文推断它(在这种情况下,是从source参数的类型中提供的)扩展方法)。

Hope this will help you. 希望这会帮助你。

Just add own validations. 只需添加自己的验证。

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

Usage: 用法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM