Skip to content

Instantly share code, notes, and snippets.

@aloraman
Created August 12, 2022 09:55
Show Gist options
  • Save aloraman/e07b532291a75be1a919bf5d936e1cbe to your computer and use it in GitHub Desktop.
Save aloraman/e07b532291a75be1a919bf5d936e1cbe to your computer and use it in GitHub Desktop.
EqualityComparerEx type from Adalon.Mediolanum
// 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