46 votes

Linq Distinct() utilise un délégué pour le comparateur d'égalité

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

Tout cela est parfait et fonctionne comme je le souhaite. Par curiosité, dois-je définir mon propre Comparer, ou puis-je le remplacer par un délégué ? Je pensais pouvoir faire quelque chose comme ceci :

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

Mais ça ne compile pas. Existe-t-il une astuce ?

104voto

driis Points 70872

Distinct prend un IEqualityComparer comme second argument, vous aurez donc besoin d'un IEqualityComparer. Il 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 dans certains endroits, comme MoreLINQ suggéré dans l'une des autres réponses.

Vous pourriez l'implémenter de la manière suivante :

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

11voto

Ani Points 59747

C'est malheureux que Distinct n'arrive pas avec une telle surcharge, donc ce que vous avez est une bonne option.

Avec PlusLinq vous pouvez utiliser l'option DistinctBy opérateur.

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

Vous pouvez également envisager de rédiger un document générique ProjectionEqualityComparer qui peut transformer le délégué approprié en un IEqualityComparer<T> comme celle qui figure dans la liste aquí .

4voto

Eric Points 2795

Voici mon petit truc pervers de vanille C# :

entities
    .GroupBy(e => e.Id)
    .Select(g => g.First())

2voto

Subhani Points 31

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

Voici le code, tiré 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();
        }
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X