HashSet constructor with custom IEqualityCompare defined by lambda?

Currently, the HashSet<T> constructor, which allows you to independently define your equality comparison, is the HashSet<T>(IEqualityComparer<T> comparer) constructor HashSet<T>(IEqualityComparer<T> comparer) . I would like to define this EqualityComparer as a lambda.

I found this blog post that created a class that allows you to generate your comparator via lambda and then hides the construction of this class using an extension method, for example for Except ().

Now I would like to do the same, but with the constructor. Is it possible to create a constructor using the extension method? Or can I somehow create a HashSet<T>(Func<T,T,int> comparer) ?

- UPDATE -
For clarity, this is an (excerpt) of a formatted version of what I'm trying to accomplish:

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

or more perfect

 HashSet<FileInfo> resultFiles = new HashSet<FileInfo>( srcPath.GetFiles(), (f1, f2) => f1.Name.SubString(10).Equals(f2.Name.SubString(10))); 
+4
source share
2 answers

No, you cannot add constructors (even using extension methods).

Assuming you have some kind of magical way to get from Func<T,T,int> to IEqualityComparer<T> (I would be interested to read this blog post if you can quote it) - then the closest thing you can probably do something like:

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

Nonetheless; I doubt what you can do with lambda for equality ... you have two concepts to express: hashing and true equality. What would your lambda look like? If you are trying to defer child properties, then maybe Func<T,TValue> to select a property, and use EqualityComparer<TValue>.Default internally ... something like:

 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)); } } 
+5
source
Mark is right. There is no easy way for one lambda to express the information needed for both Equals and GetHashCode. And if you provide a GetHashCode that returns different hashes for "equal" elements, this will cause incorrect behavior.

Here is my compromise implementation. It will allow any generic Func (for example, Marc, I ignored int, because you did not explain it), and it will give correct (in that it matches the contract), but very inefficient behavior.

I recommend that you stick with a real IEqualityComparer that suits your needs. This is a shame C # does not support anonymous inner classes.

 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); } } 
+1
source

All Articles