Created
August 12, 2022 09:55
-
-
Save aloraman/e07b532291a75be1a919bf5d936e1cbe to your computer and use it in GitHub Desktop.
EqualityComparerEx type from Adalon.Mediolanum
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// LICENSE: MIT | |
// Taken from Adalon.Mediolanum | |
// Not published yet | |
// Will be published under MIT license | |
// To use: replace ThrowHelper with other null check | |
// replace FrugalPairLocalList with other List collection | |
// Usage: EqualityComparerEx.Select((Foo f) => f.IntProperty).ChainWith(f=>f.StringProperty, StringComparer.OrdinalIgnoreCase) | |
using System; | |
using System.Collections.Generic; | |
using JetBrains.Annotations; | |
namespace Adalon.Collections | |
{ | |
[PublicAPI] | |
public static class EqualityComparerEx | |
{ | |
[Pure] | |
[NotNull] | |
public static IEqualityComparer<T> Create<T>([NotNull] Func<T, T, bool> equals, | |
[NotNull] Func<T, int> getHashCode) | |
{ | |
ThrowHelper.ThrowIfNull(equals, nameof(equals)); | |
ThrowHelper.ThrowIfNull(getHashCode, nameof(getHashCode)); | |
return new DelegatingEqualityComparer<T>(equals, getHashCode); | |
} | |
[Pure] | |
[NotNull] | |
public static IEqualityComparer<TSource> Select<TSource, TKey>([NotNull] Func<TSource, TKey> keySelector, | |
[CanBeNull] IEqualityComparer<TKey> keyEqualityComparer = null) | |
{ | |
ThrowHelper.ThrowIfNull(keySelector, nameof(keySelector)); | |
if (keyEqualityComparer == null) keyEqualityComparer = EqualityComparer<TKey>.Default; | |
return new ProjectedEqualityComparer<TSource, TKey>(keySelector, keyEqualityComparer); | |
} | |
[Pure] | |
[NotNull] | |
public static IEqualityComparer<T> ChainWith<T>([NotNull] this IEqualityComparer<T> target, | |
IEqualityComparer<T> equalityComparer) | |
{ | |
ThrowHelper.ThrowIfNull(target, nameof(target)); | |
ThrowHelper.ThrowIfNull(equalityComparer, nameof(equalityComparer)); | |
return new ChainedEqualityComparer<T>(target, equalityComparer); | |
} | |
[Pure] | |
[NotNull] | |
public static IEqualityComparer<TSource> ChainWith<TSource, TKey>( | |
[NotNull] this IEqualityComparer<TSource> target, | |
[NotNull] Func<TSource, TKey> keySelector, | |
[CanBeNull] IEqualityComparer<TKey> keyEqualityComparer = null) | |
{ | |
ThrowHelper.ThrowIfNull(target, nameof(target)); | |
ThrowHelper.ThrowIfNull(keySelector, nameof(keySelector)); | |
if (keyEqualityComparer == null) keyEqualityComparer = EqualityComparer<TKey>.Default; | |
return new ChainedEqualityComparer<TSource>(target, | |
new ProjectedEqualityComparer<TSource, TKey>(keySelector, keyEqualityComparer)); | |
} | |
private sealed class DelegatingEqualityComparer<T> : IEqualityComparer<T> | |
{ | |
private readonly Func<T, T, bool> _equals; | |
private readonly Func<T, int> _getHashCode; | |
public DelegatingEqualityComparer([NotNull] Func<T, T, bool> equals, | |
[NotNull] Func<T, int> getHashCode) | |
{ | |
_equals = equals; | |
_getHashCode = getHashCode; | |
} | |
bool IEqualityComparer<T>.Equals(T x, T y) => _equals(x, y); | |
int IEqualityComparer<T>.GetHashCode(T obj) => _getHashCode(obj); | |
} | |
private sealed class ProjectedEqualityComparer<TSource, TKey> : IEqualityComparer<TSource> | |
{ | |
private readonly Func<TSource, TKey> _keySelector; | |
private readonly IEqualityComparer<TKey> _keyEqualityComparer; | |
public ProjectedEqualityComparer( | |
[NotNull] Func<TSource, TKey> keySelector, | |
[NotNull] IEqualityComparer<TKey> keyEqualityComparer) | |
{ | |
_keySelector = keySelector; | |
_keyEqualityComparer = keyEqualityComparer; | |
} | |
bool IEqualityComparer<TSource>.Equals(TSource x, TSource y) => | |
_keyEqualityComparer.Equals(_keySelector(x), _keySelector(y)); | |
int IEqualityComparer<TSource>.GetHashCode(TSource source) | |
{ | |
var key = _keySelector(source); | |
return key == null ? 0 : _keyEqualityComparer.GetHashCode(key); | |
} | |
} | |
private sealed class ChainedEqualityComparer<T> : IEqualityComparer<T> | |
{ | |
private readonly FrugalPairLocalList<IEqualityComparer<T>> _links; | |
public ChainedEqualityComparer(IEqualityComparer<T> baseComparer, IEqualityComparer<T> newLink) | |
{ | |
if (baseComparer is ChainedEqualityComparer<T> chained) | |
{ | |
_links = new FrugalPairLocalList<IEqualityComparer<T>>(chained._links) { newLink }; | |
} | |
else | |
{ | |
_links = new FrugalPairLocalList<IEqualityComparer<T>>(baseComparer, newLink); | |
} | |
} | |
bool IEqualityComparer<T>.Equals(T x, T y) | |
{ | |
var result = true; | |
foreach (var comparer in _links) | |
{ | |
result &= comparer.Equals(x, y); | |
if (!result) break; | |
} | |
return result; | |
} | |
int IEqualityComparer<T>.GetHashCode(T obj) | |
{ | |
var result = 0; | |
foreach (var comparer in _links) | |
{ | |
result = (result * 397) ^ comparer.GetHashCode(obj); | |
} | |
return result; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment