_From @ViIvanov on December 14, 2017 20:15_
Now we have a Create method in Comparer<> class:
public static Comparer<T> Create(Comparison<T> comparison)
I think, it will be usable to have an analog in EqualityComparer<>:
public static EqualityComparer<T> Create(Func<T, T, bool> equals, Func<T, int> getHashCode)
_Copied from original issue: dotnet/coreclr#15526_
Not quite as handy since we need two delegates, and there isn't as much of an existing use as there is with Comparison, but I could definitely see this as something I'd use while experimenting at least, even if I might end up replacing with a custom equality comparer.
What is the value? Example of usage today vs. in future? How commonly is it used?
Ideally please follow formal API proposal (see the example there).
From a brief look it seems today's state - inheritance + overriding 2 methods is not too much extra code compared to the API proposed above.
The case of Comparer is more needed IMO to avoid adding another class with boilerplate.
I also find that very useful.
It's annoying when you always have to implement an interface to be able to compare two property values.
Though I'd prefer the method to be in a shorter version:
```c#
public static IEqualityComparer
Which will compare the two objects if the specified properties are equal, and will generate hash-codes based on the provided lambda properties.
Usage:
```c#
var eqComparer1 = EqualityComparer<Contact>.Create(c => c.FullName);
var eqComparer2 = EqualityComparer<Contact>.Create(c => c.FirstName, c => c.LastName);
Here's some untested prototype:
```c#
public class EqualityComparerImpl
{
public static EqualityComparerImpl
params Expression
new EqualityComparerImpl
PropertyInfo[] _properties;
EqualityComparerImpl(Expression
{
if (properties == null)
throw new ArgumentNullException(nameof(properties));
if (properties.Length == 0)
throw new ArgumentOutOfRangeException(nameof(properties));
var length = properties.Length;
var extractions = new PropertyInfo[length];
for (int i = 0; i < length; i++)
{
var property = properties[i];
extractions[i] = ExtractProperty(property);
}
_properties = extractions;
}
public bool Equals(T x, T y)
{
if (ReferenceEquals(x, y))
//covers both are null
return true;
if (x == null || y == null)
return false;
var len = _properties.Length;
for (int i = 0; i < _properties.Length; i++)
{
var property = _properties[i];
if (!Equals(property.GetValue(x), property.GetValue(y)))
return false;
}
return true;
}
public int GetHashCode(T obj)
{
if (obj == null)
return 0;
var hashes = _properties.Select(pi => pi.GetValue(obj)?.GetHashCode() ?? 0).ToArray();
return Combine(hashes);
}
static int Combine(int[] hashes)
{
int result = 0;
foreach (var hash in hashes)
{
uint rol5 = ((uint)result << 5) | ((uint)result >> 27);
result = ((int)rol5 + result) ^ hash;
}
return result;
}
static PropertyInfo ExtractProperty(Expression
{
if (property.NodeType != ExpressionType.Lambda)
throwEx();
var body = property.Body;
if (body.NodeType == ExpressionType.Convert)
if (body is UnaryExpression unary)
body = unary.Operand;
else
throwEx();
if (!(body is MemberExpression member))
throwEx();
if (!(member.Member is PropertyInfo pi))
throwEx();
return pi;
void throwEx() =>
throw new NotSupportedException($"The expression '{property}' isn't supported.");
}
}
```
And BTW, there IS demand for this.
Is it hard to write a class implementing IEqualityComparer<T>? Nope. But it's tedious, and wordy.
That's why in every private "util" library I use, I eventually wind up with a delegate-based implementation of IEqualityComparer<T> that has a Create() method to accept the appropriate lambda expression delegates for the Equals() and GetHashCode() implementations. I get to write (copy/paste) that class in once, and then when I need an equality comparer, it's a one-liner in the actual code.
And to be clear: writing a new IEqualityComparer<T> for each use case in a non-starter. It's way too much extra code, and it hides the implementation details away in some other class, when what you really want is to be able to see those right where the code is used.
Does it come up a _ton_? Again, nope. I may need this maybe two or three times a year, tops. But then, so what? It's such a small thing to add, and yet a glaring omission, from the otherwise very helpful and convenient .NET API.
Is there a place to vote for this? As you can see, my vote would be a solid "yes".
@peterduniho upvote top post. It is sortable on GitHub by number of votes there ...
@karelz thanks. I don't see an "upvote" option per se, but I assume you mean the "thumbs up reaction" option. "Vote" submitted. :)
Can't even write a .Distinct(..) without also writing up a whole new IEqualityComparer class implementation, just for one LINQ clause, this is ridiculous.
Maybe Distinct(..) itself should tuck this away with its own overload(s) with a single lambda or a growable (params style) set of lambdas listing all the properties to find distinction on for just this one filter statement. The output of this being a generated implementation of IEqualityComparer. In which case I suppose it would be handled by a different team / group with a different github work item.
Can someone update the sample to make a compelling case for the currently proposed API? The sample code was for a different proposal.
If Distinct is the only use case, then maybe we should fix Distinct itself.
Can someone update the sample to make a compelling case for the currently proposed API? The sample code was for a different proposal.
If Distinct is the only use case, then maybe we should fix Distinct itself.
I'm not sure what sample you mean, so I'm probably missing part of the point of the request. My apologies about that.. But this isn't just an issue with Distinct(). Another common scenario would be various dictionary types (e.g. Dictionary<TKey, TValue> and ConcurrentDictionary<TKey, TValue>). Pretty much any place you'd want an instance of IEqualityComparer<T>, it would be much nicer to be able to quickly define one using lambdas, rather than having to implement a whole new class every time.
The fact that utility libriaries used internally in numerous projects have defined this functionality over and over strongly suggests it'd be a useful addition to the API (for what it's worth, this has come up in pretty much every non-trivial project I've been involved with over the last ten years or so).
Yes, it's true that this could be addressed in other ways. For example, just forget about it and let everyone keep doing what they've been doing (i.e. create the helper methods themselves). Or each of the places in .NET where IEqualityComparer<T> can be used can add an overload where instead of passing IEqualityComparer<T>, one can pass the necessary lambdas to define the comparer. But putting this in a single centralized place where it can be used for any place one needs IEqualityComparer<T> seems to me to be a lot more useful approach.
Given where we are at in 5.0.0 I don't this this will make it. I do support the scenario, I've come across this before and have felt it jarring that I need to stop coding and create a new type, just when I want to tweak the comparison behavior of a collection.
Most helpful comment
Can't even write a .Distinct(..) without also writing up a whole new IEqualityComparer class implementation, just for one LINQ clause, this is ridiculous.
Maybe Distinct(..) itself should tuck this away with its own overload(s) with a single lambda or a growable (params style) set of lambdas listing all the properties to find distinction on for just this one filter statement. The output of this being a generated implementation of IEqualityComparer. In which case I suppose it would be handled by a different team / group with a different github work item.