You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
IEqualityComparer for sequences (T[], Memory<T>, IEnumerable<T>) #44796
Sometimes, it is useful to use arrays as keys in a map such as Dictionary. A motivating use case would be blockchain applications. Here, we often need to map a hash to some piece of data. For example, we might want to map a 32 byte block hash (byte[]) to block metadata.
Proposal:
There should be built-in equality comparers for sequences. By default, each element is default-compared. It should be possible to use a custom nested comparer.
I propose this API surface:
public static class EqualityComparer
{
public static IEqualityComparer<T[]> Array<T>(IEqualityComparer<T> elementComparer = null);
public static IEqualityComparer<Memory<T>> Memory<T>(IEqualityComparer<T> elementComparer = null);
public static IEqualityComparer<IEnumerable<T>> Sequence<T>(IEqualityComparer<T> elementComparer = null);
}
Example usage:
var dict = new Dictionary<byte[], BlockHeader>(EqualityComparer.Array<byte>());
These factory methods can internally pick an optimal comparer for the given arguments. For example, for arrays of primitive types, we can use memcmp-style algorithms internally. If a custom comparer is provided, an element-by-element comparison algorithm must be used. A cached instance can be used if no custom comparer is provided.
Possible extension: The same thing could be done for IComparer<T[]> (see #33873).
Reacted by strangeman375, David Piepgrass, kronic, Antonio Freire and Paulus Pärssinen
Should we be also supporting ReadOnlyMemory<T> comparers?
Is there a case that Memory<T> needs to be treated differently from ReadOnlyMemory<T>? If no, then I'd say to only have ReadOnlyMemory<T> comparers, since converting from the writable version to ReadOnly is implicit.
Any annotation to clarify it's sequence equality instead of set equality?
Hmm, I can't think of a reason why this could be assumed to use set equality (or why would ever want to introduce set equality for these types to require some form of disambiguation). But perhaps you're right that we should be explicit about the semantics in the combinator names. But I can't think of a naming convention better than this:
namespace System.Collections.Generic
{
+ public static class EqualityComparer+ {+ public static IEqualityComparer<T[]> ArraySequenceEquality<T>(IEqualityComparer<T>? elementComparer = null);+ public static IEqualityComparer<ReadOnlyMemory<T>> MemorySequenceEquality<T>(IEqualityComparer<T>? elementComparer = null);+ public static IEqualityComparer<IEnumerable<T>> EnumerableSequenceEquality<T>(IEqualityComparer<T>? elementComparer = null);+ }
}
@Joe4evr A Dictionary with Memory<T> keys would need a Memory<T> equality comparer, since implicit coersion to ReadOnlyMemory doesn't work inside generic code. Arguably one should not use Memory<T> keys, though.
That would double the number of interface calls per comparison, reducing performance. Keep in mind: performance is what motivates the use of Memory<T> in the first place.
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
Activity
Dotnet-GitSync-Bot commentedon Nov 17, 2020
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
huoyaoyuan commentedon Nov 17, 2020
I don't think adding excessive overloads for matrix of current collections and operations is a good idea.
I'm looking forward to improvement of type system (HKT, Type Classes). Then they can be written once for all kind of certain abstraction.
eiriktsarpalis commentedon Nov 25, 2020
I think the request seems reasonable. The API design should probably be made in unison with #33873.
eiriktsarpalis commentedon Nov 25, 2020
Should we be also supporting
ReadOnlyMemory<T>comparers?Joe4evr commentedon Nov 26, 2020
Is there a case that
Memory<T>needs to be treated differently fromReadOnlyMemory<T>? If no, then I'd say to only haveReadOnlyMemory<T>comparers, since converting from the writable version to ReadOnly is implicit.eiriktsarpalis commentedon Nov 26, 2020
Agreed, so perhaps something like this?
namespace System.Collections.Generic { + public static class EqualityComparer + { + public static IEqualityComparer<T[]> Array<T>(IEqualityComparer<T>? elementComparer = null); + public static IEqualityComparer<ReadOnlyMemory<T>> ReadOnlyMemory<T>(IEqualityComparer<T>? elementComparer = null); + public static IEqualityComparer<IEnumerable<T>> Sequence<T>(IEqualityComparer<T>? elementComparer = null); + } }huoyaoyuan commentedon Nov 26, 2020
Any annotation to clarify it's sequence equality instead of set equality?
eiriktsarpalis commentedon Nov 26, 2020
Hmm, I can't think of a reason why this could be assumed to use set equality (or why would ever want to introduce set equality for these types to require some form of disambiguation). But perhaps you're right that we should be explicit about the semantics in the combinator names. But I can't think of a naming convention better than this:
namespace System.Collections.Generic { + public static class EqualityComparer + { + public static IEqualityComparer<T[]> ArraySequenceEquality<T>(IEqualityComparer<T>? elementComparer = null); + public static IEqualityComparer<ReadOnlyMemory<T>> MemorySequenceEquality<T>(IEqualityComparer<T>? elementComparer = null); + public static IEqualityComparer<IEnumerable<T>> EnumerableSequenceEquality<T>(IEqualityComparer<T>? elementComparer = null); + } }eiriktsarpalis commentedon Apr 19, 2021
Related to #48304.
qwertie commentedon Jul 25, 2021
@Joe4evr A
DictionarywithMemory<T>keys would need aMemory<T>equality comparer, since implicit coersion toReadOnlyMemorydoesn't work inside generic code. Arguably one should not useMemory<T>keys, though.Joe4evr commentedon Jul 26, 2021
@qwertie If you absolutely need that for some ungodly reason, you can make a wrapper easily enough:
qwertie commentedon Jul 26, 2021
That would double the number of interface calls per comparison, reducing performance. Keep in mind: performance is what motivates the use of
Memory<T>in the first place.eiriktsarpalis commentedon Oct 29, 2021
Related to #27039.
dotnet-policy-service commentedon Oct 19, 2026
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
This process is part of our issue cleanup automation.
dotnet-policy-service commentedon Oct 19, 2026
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
This process is part of our issue cleanup automation.