LINQ的Distinct()的相等比较器使用委托

我有一个使用我自己的自定义比较器的LINQ Distinct()语句,如下所示:

class MyComparer<T> : IEqualityComparer<T> where T : MyType
{
    public bool Equals(T x, T y)
    {
        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(T obj)
    {
        return obj.Id.GetHashCode();
    }
}

...

var distincts = bundle.GetAllThings.Distinct(new MyComparer<MySubType>());

这一切都很好,很花哨,可以按我的意愿工作。 出于好奇,我是否需要定义自己的比较器,还是可以用委托替换它? 我以为我应该可以做这样的事情:

var distincts = bundle.GetAllThings.Distinct((a,b) => a.Id == b.Id);

但这无法编译。 有整洁的把戏吗?

Aidan asked 2020-06-29T21:01:42Z
4个解决方案
104 votes

Distinct将IEqualityComparer作为第二个参数,因此您将需要IEqualityComparer。 不过,创建一个可以派代表的通用类并不是一件容易的事。 当然,这可能已经在某些地方实现,例如在其他答案之一中建议的MoreLINQ。

您可以像这样实现它:

public static class Compare
{
    public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector)
    {
        return source.Distinct(Compare.By(identitySelector));
    }

    public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector)
    {
        return new DelegateComparer<TSource, TIdentity>(identitySelector);
    }

    private class DelegateComparer<T, TIdentity> : IEqualityComparer<T>
    {
        private readonly Func<T, TIdentity> identitySelector;

        public DelegateComparer(Func<T, TIdentity> identitySelector)
        {
            this.identitySelector = identitySelector;
        }

        public bool Equals(T x, T y)
        {
            return Equals(identitySelector(x), identitySelector(y));
        }

        public int GetHashCode(T obj)
        {
            return identitySelector(obj).GetHashCode();
        }
    }
}

语法如下:

source.DistinctBy(a => a.Id);

或者,如果您觉得这样更清晰:

source.Distinct(Compare.By(a => a.Id));
driis answered 2020-06-29T21:02:09Z
11 votes

不幸的是ProjectionEqualityComparer没有提供这样的重载,因此您拥有的是一个不错的选择。

通过MoreLinq,可以使用ProjectionEqualityComparer运算符。

var distincts = bundle.GetAllThings.DistinctBy(a => a.Id); 

您可能还需要考虑编写一个通用的ProjectionEqualityComparer,它可以将相应的委托转换为IEqualityComparer<T>的实现,例如此处列出的实现。

Ani answered 2020-06-29T21:02:38Z
2 votes

该链接显示了如何创建扩展方法以能够以您指定的方式使用Distinct。 您将需要编写两个Distinct扩展方法和一个IEqualityComparer

这是来自站点的代码:

public static class Extensions
    {
        public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer)
        {           
            return source.Distinct(new DelegateComparer<T>(comparer));
        }

        public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer, Func<T,int> hashMethod)
        {
            return source.Distinct(new DelegateComparer<T>(comparer,hashMethod));
        }
    }

    public class DelegateComparer<T> : IEqualityComparer<T>
    {
        private Func<T, T, bool> _equals;
        private Func<T,int> _getHashCode;

        public DelegateComparer(Func<T, T, bool> equals)
        {
            this._equals = equals;
        }

        public DelegateComparer(Func<T, T, bool> equals, Func<T,int> getHashCode)
        {
            this._equals = equals;
            this._getHashCode = getHashCode;
        }

        public bool Equals(T a, T b)
        {
            return _equals(a, b);
        }

        public int GetHashCode(T a)
        {
            if (_getHashCode != null)       
                return _getHashCode(a);       
            else
                return a.GetHashCode();
        }
    }
Ahmed Subhani answered 2020-06-29T21:03:02Z
-1 votes

这是我不道德的肮脏的小香草C#技巧:

entities
    .GroupBy(e => e.Id)
    .Select(g => g.First())
Eric answered 2020-06-29T21:03:22Z
translate from https://stackoverflow.com:/questions/4607485/use-a-delegate-for-the-equality-comparer-for-linqs-distinct