web-dev-qa-db-fra.com

Utiliser un délégué pour le comparateur d'égalité pour LINQ's Distinct ()

J'ai une instruction LINQ Distinct () qui utilise mon propre comparateur personnalisé, comme ceci:

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

C'est très bien et dandy et fonctionne comme je veux. Par curiosité, dois-je définir mon propre Comparer ou puis-je le remplacer par un délégué? Je pensais que je devrais être capable de faire quelque chose comme ça:

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

Mais cela ne compile pas. Y a-t-il une astuce intéressante?

45
Aidan

Distinct prend un IEqualityComparer comme deuxième argument, vous aurez donc besoin d'un IEqualityComparer. Ce n'est pas trop difficile d'en faire un générique qui prendra un délégué, cependant. Bien sûr, cela a probablement déjà été mis en œuvre à certains endroits, comme MoreLINQ l'a suggéré dans l'une des autres réponses.

Vous pouvez l'implémenter quelque chose comme ceci:

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

Ce qui vous donne la syntaxe:

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

Ou, si vous pensez que c'est plus clair de cette façon:

source.Distinct(Compare.By(a => a.Id));
104
driis

Il est regrettable que Distinct ne présente pas une telle surcharge, donc ce que vous avez est une bonne option.

Avec MoreLinq , vous pouvez utiliser l'opérateur DistinctBy .

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

Vous pouvez également envisager d'écrire un ProjectionEqualityComparer générique qui peut transformer le délégué approprié en IEqualityComparer<T> implémentation, comme celle listée ici .

11
Ani

Ce lien montre comment créer la méthode d'extension pour pouvoir utiliser Distinct de la manière que vous avez donnée. Vous devrez écrire deux méthodes d'extension Distinct et une IEqualityComparer.

Voici le code du site:

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();
        }
    }
2
Ahmed Subhani