Pergunta

Atualmente, o construtor HashSet<T> que lhe permite definir sua comparação de igualdade si mesmo é o construtor HashSet<T>(IEqualityComparer<T> comparer). Gostaria de definir esta EqualityComparer como um lambda.

este post , que fez uma classe que permite gerar sua comparer através lambda e depois esconde a construção desta classe com um método de extensão para fazer, por exemplo, uma exceção ().

Agora eu gostaria de fazer o mesmo, mas com um construtor. É possível criar um construtor através de um método de extensão? Ou existe outra maneira que eu poderia de alguma forma criar um HashSet<T>(Func<T,T,int> comparer)?

- ATUALIZAÇÃO -
Para maior clareza, este é (um trecho de) uma versão mão livre do que eu estou tentando fazer:

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

ou mais idealmente

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

Solução

Não, você não pode adicionar construtores (mesmo com os métodos de extensão).

Supondo que você tenha alguma maneira mágica para obter de um Func<T,T,int> a um IEqualityComparer<T> (eu estaria interessado em ler que post se você pode citar-lo) -, em seguida, o mais próximo que você pode fazer é provavelmente algo como:

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

No entanto; Estou duvidosa quanto ao que você pode fazer com um lambda para a igualdade ... você tem dois conceitos para expressar: hash e verdadeira igualdade. Qual seria o seu olhar lambda como? Se você está tentando adiar para propriedades filho, então talvez um Func<T,TValue> para selecionar a propriedade e uso EqualityComparer<TValue>.Default internamente ... algo como:

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

Outras dicas

Marc é certo. Não há nenhuma maneira simples para um único lambda para expressar a informação necessária para ambos os Equals e GetHashCode. E se você fornecer um GetHashCode que os retornos diferentes hashes para elementos "iguais", que irá causar um comportamento incorreto.

Aqui está minha implementação compromisso. Ele permitirá que qualquer Func genérico (como Marc, eu ignorou o int porque você não explicá-lo), e que lhe dará correta (na medida em que está em conformidade com o contrato), mas o comportamento muito ineficiente.

Eu recomendo que você ficar com um IEqualityComparer real que atenda às suas necessidades. É uma vergonha C # não suporta classes internas anônimas, no entanto.

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);
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top