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

2019-07-30 07:04发布

我写这整齐类实现的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);

这也不起作用。 类型/命名空间未找到(红色下划线)。

我如何得到这个直?

Answer 1:

我将首先提供非匿名类型的解决方案,后来它扩大到匿名类型的正常工作。 我们希望,它会帮助你了解什么人试图在评论你的问题说了。

我的“万能” 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在编译时。 相反,你需要使用类型推断 ,让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参数扩展方法)。

希望这会帮助你。



Answer 2:

只需添加自己的验证。

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


文章来源: Implementing IEqualityComparer for comparing arbitrary properties of any class (including anonymous)