Конструктор HashSet с пользовательским IEqualityCompare, определенным с помощью lambda?
-
22-08-2019 - |
Вопрос
В настоящее время HashSet<T>
конструктор, который позволяет вам самостоятельно определять ваше сравнение на равенство, - это HashSet<T>(IEqualityComparer<T> comparer)
конструктор.Я хотел бы определить этот EqualityComparer как лямбда.
Я нашел это сообщение в блоге это создало класс, который позволяет вам генерировать ваш компаратор через lambda, а затем скрывает построение этого класса с помощью метода расширения для выполнения, например, 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)));
Решение
Нет, вы не можете добавлять конструкторы (даже с помощью методов расширения).
Предполагая, что у вас есть какой-то волшебный способ получить от 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));
}
}
Другие советы
Марк прав.Не существует простого способа для одного лямбда-выражения выразить информацию, необходимую как для Equals, так и для GetHashCode.И если вы предоставите GetHashCode, который возвращает разные хэши для "равных" элементов, это вызовет некорректное поведение.
Вот моя компромиссная реализация.Это позволит использовать любую общую функцию (например, Marc, я проигнорировал 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);
}
}