Конструктор HashSet с пользовательским IEqualityCompare определяется lambda? - PullRequest
4 голосов
/ 19 мая 2009

В настоящее время конструктор HashSet<T>, который позволяет самостоятельно определять сравнение, является конструктором HashSet<T>(IEqualityComparer<T> comparer). Я хотел бы определить этот EqualityComparer как лямбда.

Я нашел это сообщение в блоге , в котором создан класс, который позволяет генерировать ваш компаратор с помощью лямбды, а затем скрывает конструкцию этого класса с помощью метода расширения, например, для Except ().

Теперь я хотел бы сделать то же самое, но с конструктором. Можно ли создать конструктор с помощью метода расширения? Или есть другой способ, которым я мог бы как-то создать HashSet<T>(Func<T,T,int> comparer)?

- ОБНОВЛЕНИЕ -
Для ясности, это (фрагмент) версии от руки того, что я пытаюсь выполнить:

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

или более идеально

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

Ответы [ 2 ]

5 голосов
/ 19 мая 2009

Нет, вы не можете добавлять конструкторы (даже с методами расширения).

Предполагая, что у вас есть какой-то волшебный способ перейти от Func<T,T,int> к IEqualityComparer<T> (мне было бы интересно почитать это сообщение в блоге, если вы можете его процитировать) - тогда самое близкое, что вы можете сделать, это что-то вроде

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

Тем не менее, Я сомневаюсь, что вы можете сделать с лямбда для равенства ... у вас есть две концепции, чтобы выразить: хеширование и истинное равенство. Как будет выглядеть ваша лямбда? Если вы пытаетесь отложить до дочерних свойств, то, возможно, Func<T,TValue>, чтобы выбрать свойство, и использовать EqualityComparer<TValue>.Default внутренне ... что-то вроде:

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));
    }
}
1 голос
/ 19 мая 2009

Марк прав. Для одной лямбды нет простого способа выразить информацию, необходимую как для Equals, так и для GetHashCode. И если вы предоставите GetHashCode, который возвращает разные хэши для «равных» элементов, это приведет к некорректному поведению.

Вот моя компромиссная реализация. Это позволит любой универсальный Func (например, Марк, я игнорировал int, потому что вы его не объяснили), и это даст правильное (в том смысле, что оно соответствует контракту), но очень неэффективное поведение.

Я рекомендую вам придерживаться настоящего IEqualityComparer, который отвечает вашим потребностям. Обидно, что C # не поддерживает анонимные внутренние классы.

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