Подтвердить что ты не робот

Используйте делегат для сопоставления равенства для 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>());

Это все прекрасно и денди и работает так, как я хочу. Из любопытства мне нужно определить свой собственный Comparer, или я могу заменить его делегатом? Я думал, что смогу сделать что-то вроде этого:

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

Но это не компилируется. Есть ли опрятный трюк?

4b9b3361

Ответ 1

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

Ответ 2

Несчастливо, что Distinct не вызывает такую ​​перегрузку, поэтому у вас есть хороший вариант.

С MoreLinq вы можете использовать DistinctBy.

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

Возможно, вам захочется также написать общий ProjectionEqualityComparer, который может превратить соответствующий делегат в реализацию IEqualityComparer<T>, например, указанную здесь.

Ответ 3

Эта ссылка показывает, как создать метод расширения, чтобы иметь возможность использовать 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();
        }
    }