Question

Actuellement, le constructeur de HashSet<T> qui vous permet de définir votre comparaison d'égalité vous est le constructeur de HashSet<T>(IEqualityComparer<T> comparer). Je voudrais définir cette EqualityComparer comme lambda.

J'ai trouvé ce blog qui a fait une classe qui vous permet de générer votre comparateur par lambda et cache la construction de cette classe avec une méthode extention à faire par exemple un Sauf ().

Maintenant, je voudrais faire la même chose mais avec un constructeur. Est-il possible de créer un constructeur par une méthode extention? Ou est-il une autre façon que je pouvais en quelque sorte créer un HashSet<T>(Func<T,T,int> comparer)?

- MISE À JOUR - Pour plus de clarté, c'est (un extrait de) une version Freehand de ce que je suis en train d'accomplir:

HashSet<FileInfo> resultFiles = new HashSet<FileInfo>(
    srcPath.GetFiles(),
    new LambdaComparer<FileInfo>(
        (f1, f2) => f1.Name.SubString(10).Equals(f2.Name.SubString(10))));

ou plus idéalement

HashSet<FileInfo> resultFiles = new HashSet<FileInfo>(
    srcPath.GetFiles(),
    (f1, f2) => f1.Name.SubString(10).Equals(f2.Name.SubString(10)));
Était-ce utile?

La solution

Non, vous ne pouvez pas ajouter des constructeurs (même avec des méthodes d'extension).

En supposant que vous avez une certaine façon magique pour obtenir d'un Func<T,T,int> à un IEqualityComparer<T> (je serais intéressé à lire ce poste de blog si vous pouvez le citer) - alors le plus proche que vous pouvez faire est probablement quelque chose comme:

public static class HashSet {
    public static HashSet<T> Create<T>(Func<T, T, int> func) {
        IEqualityComparer<T> comparer = YourMagicFunction(func);
        return new HashSet<T>(comparer);
    }
}

Cependant; Je suis douteuse quant à ce que vous pouvez faire avec un lambda pour l'égalité ... vous avez deux concepts pour exprimer: hash, et une véritable égalité. Quelles seraient vos lambda ressembler? Si vous essayez de reporter les propriétés de l'enfant, alors peut-être un Func<T,TValue> pour sélectionner la propriété et l'utilisation EqualityComparer<TValue>.Default interne ... quelque chose comme:

class Person {
    public string Name { get; set; }
    static void Main() {
        HashSet<Person> people = HashSetHelper<Person>.Create(p => p.Name);
        people.Add(new Person { Name = "Fred" });
        people.Add(new Person { Name = "Jo" });
        people.Add(new Person { Name = "Fred" });
        Console.WriteLine(people.Count);
    }
}
public static class HashSetHelper<T> {
    class Wrapper<TValue> : IEqualityComparer<T> {
        private readonly Func<T, TValue> func;
        private readonly IEqualityComparer<TValue> comparer;
        public Wrapper(Func<T, TValue> func,
            IEqualityComparer<TValue> comparer) {
            this.func = func;
            this.comparer = comparer ?? EqualityComparer<TValue>.Default;
        }
        public bool Equals(T x, T y) {
            return comparer.Equals(func(x), func(y));
        }

        public int GetHashCode(T obj) {
            return comparer.GetHashCode(func(obj));
        }
    }
    public static HashSet<T> Create<TValue>(Func<T, TValue> func) {
        return new HashSet<T>(new Wrapper<TValue>(func, null));
    }
    public static HashSet<T> Create<TValue>(Func<T, TValue> func,
        IEqualityComparer<TValue> comparer)
    {
        return new HashSet<T>(new Wrapper<TValue>(func, comparer));
    }
}

Autres conseils

Marc est juste. Il n'y a aucun moyen simple pour un seul lambda pour exprimer les informations nécessaires pour les Equals et GetHashCode. Et si vous fournissez un GetHashCode qui retourne différents hash pour les éléments « égaux », qui va provoquer un comportement incorrect.

Voici ma mise en œuvre de compromis. Il permettra à tout Func générique (comme Marc, j'omis de tenir compte int parce que vous n'avez pas expliqué), et qui donnera correcte (en ce qu'elle est conforme au contrat), mais un comportement très inefficace.

Je vous recommande de tenir à un vrai IEqualityComparer qui répond à vos besoins. Il est dommage C # ne prend pas en charge les classes internes anonymes, cependant.

public static class HashSetDelegate
{
    public static HashSet<T> Create<T>(Func<T, T, bool> func)
    {
    return new HashSet<T>(new FuncIEqualityComparerAdapter<T>(func));
    }

    private class FuncIEqualityComparerAdapter<U> : IEqualityComparer<U>
    {
    private Func<U, U, bool> func;
    public FuncIEqualityComparerAdapter(Func<U, U, bool> func)
    {
        this.func = func;
    }

    public bool Equals(U a, U b)
    {
        return func(a, b);
    }

    public int GetHashCode(U obj)
    {
        return 0;
    }  

    }
}

public class HashSetTest
{
    public static void Main()
    {
    HashSet<string> s = HashSetDelegate.Create((string a, string b) => string.Compare(a, b, true) == 0);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top