Last active
May 30, 2016 21:09
-
-
Save Porges/8f52ac9b7f8984da1b241dc15647c9b6 to your computer and use it in GitHub Desktop.
Comparisons
This file contains hidden or 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
using System; | |
using Comparisons; | |
namespace Types | |
{ | |
class Person : Comparable<Person> | |
{ | |
private static readonly Comparer<Person> _comparer = | |
Comparer<Person> | |
.On(_ => _.LastName, StringComparer.OrdinalIgnoreCase) | |
.ThenOn(_ => _.FirstName, StringComparer.OrdinalIgnoreCase); | |
protected override Comparer<Person> Comparer => _comparer; | |
public string FirstName { get; } | |
public string LastName { get; } | |
public Person(string firstName, string lastName) | |
{ | |
if (firstName == null) throw new ArgumentNullException(nameof(firstName)); | |
if (lastName == null) throw new ArgumentNullException(nameof(lastName)); | |
FirstName = firstName; | |
LastName = lastName; | |
} | |
} | |
} |
This file contains hidden or 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
using System; | |
using System.Collections.Generic; | |
namespace Comparisons | |
{ | |
// Equality and Comparer only really exist because IComparer<T> doesn't inherit from IEquatable<T> | |
public class Equality<T> : IEqualityComparer<T> | |
{ | |
protected readonly IEqualityComparer<T> _equality; | |
internal Equality(IEqualityComparer<T> equality) | |
{ | |
_equality = equality; | |
} | |
public bool Equals(T x, T y) => _equality.Equals(x, y); | |
public int GetHashCode(T obj) => _equality.GetHashCode(obj); | |
public static Equality<T> On<TKey>(Func<T, TKey> extractor) | |
=> On(extractor, EqualityComparer<TKey>.Default); | |
public static Equality<T> On<TKey>(Func<T, TKey> extractor, IEqualityComparer<TKey> comparer) | |
=> new Equality<T>(new LambdaEquality<T, TKey>(extractor, comparer)); | |
public Equality<T> ThenOn<TKey>(Func<T, TKey> extractor) | |
=> ThenOn(extractor, EqualityComparer<TKey>.Default); | |
public Equality<T> ThenOn<TKey>(Func<T, TKey> extractor, IEqualityComparer<TKey> comparer) | |
=> new Equality<T>(new ChainedEquality<T>(_equality, new LambdaEquality<T, TKey>(extractor, comparer))); | |
} | |
// 'Comparison' is already stolen by the System namespace | |
public sealed class Comparer<T> : Equality<T>, IComparer<T> | |
{ | |
private readonly IComparer<T> _comparison; | |
internal Comparer(IEqualityComparer<T> equality, IComparer<T> comparison) | |
: base(equality) | |
{ | |
_comparison = comparison; | |
} | |
public int Compare(T x, T y) => _comparison.Compare(x, y); | |
public new static Comparer<T> On<TKey>(Func<T, TKey> extractor) | |
=> On(extractor, EqualityComparer<TKey>.Default, System.Collections.Generic.Comparer<TKey>.Default); | |
public static Comparer<T> On<TKey>(Func<T, TKey> extractor, IEqualityComparer<TKey> equality, IComparer<TKey> comparer) | |
=> new Comparer<T>( | |
new LambdaEquality<T, TKey>(extractor, equality), | |
new LambdaComparer<T, TKey>(extractor, comparer)); | |
public new Comparer<T> ThenOn<TKey>(Func<T, TKey> extractor) | |
=> ThenOn(extractor, EqualityComparer<TKey>.Default, System.Collections.Generic.Comparer<TKey>.Default); | |
public Comparer<T> ThenOn<TKey>(Func<T, TKey> extractor, Comparer<TKey> comparer) | |
=> ThenOn(extractor, comparer._equality, comparer._comparison); | |
public Comparer<T> ThenOn<TKey>(Func<T, TKey> extractor, IEqualityComparer<TKey> equality, IComparer<TKey> comparer) | |
=> new Comparer<T>( | |
new ChainedEquality<T>(_equality, new LambdaEquality<T,TKey>(extractor, equality)), | |
new ChainedComparer<T>(_comparison, new LambdaComparer<T,TKey>(extractor, comparer))); | |
// StringComparer is both IEqualityComparer and IComparer, as desired: | |
public static Comparer<T> On(Func<T, string> extractor, StringComparer comparer) | |
=> On(extractor, comparer, comparer); | |
public Comparer<T> ThenOn(Func<T, string> extractor, StringComparer comparer) | |
=> ThenOn(extractor, comparer, comparer); | |
} | |
// Helper classes | |
internal sealed class LambdaComparer<T, TKey> : IComparer<T> | |
{ | |
private readonly Func<T, TKey> _extractor; | |
private readonly IComparer<TKey> _comparer; | |
public LambdaComparer(Func<T, TKey> extractor, IComparer<TKey> comparer) | |
{ | |
_extractor = extractor; | |
_comparer = comparer; | |
} | |
public int Compare(T x, T y) | |
{ | |
if (x == null) | |
{ | |
if (y == null) | |
{ | |
return 0; | |
} | |
return -1; | |
} | |
if (y == null) | |
{ | |
return 1; | |
} | |
return _comparer.Compare(_extractor(x), _extractor(y)); | |
} | |
} | |
internal sealed class LambdaEquality<T, TKey> : IEqualityComparer<T> | |
{ | |
private readonly Func<T, TKey> _extractor; | |
private readonly IEqualityComparer<TKey> _comparer; | |
public LambdaEquality(Func<T, TKey> extractor, IEqualityComparer<TKey> comparer) | |
{ | |
_extractor = extractor; | |
_comparer = comparer; | |
} | |
public bool Equals(T x, T y) | |
{ | |
if (ReferenceEquals(x, y)) | |
{ | |
return true; | |
} | |
if (x == null || y == null) | |
{ | |
return false; | |
} | |
return _comparer.Equals(_extractor(x), _extractor(y)); | |
} | |
public int GetHashCode(T obj) => obj == null ? 0 : _comparer.GetHashCode(_extractor(obj)); | |
} | |
internal sealed class ChainedComparer<T> : IComparer<T> | |
{ | |
private readonly IComparer<T> _previous; | |
private readonly IComparer<T> _next; | |
public ChainedComparer(IComparer<T> previous, IComparer<T> next) | |
{ | |
_previous = previous; | |
_next = next; | |
} | |
public int Compare(T x, T y) | |
{ | |
var first = _previous.Compare(x, y); | |
if (first != 0) | |
{ | |
return first; | |
} | |
return _next.Compare(x, y); | |
} | |
} | |
internal sealed class ChainedEquality<T> : IEqualityComparer<T> | |
{ | |
private readonly IEqualityComparer<T> _previous; | |
private readonly IEqualityComparer<T> _next; | |
public ChainedEquality(IEqualityComparer<T> previous, IEqualityComparer<T> next) | |
{ | |
_previous = previous; | |
_next = next; | |
} | |
public bool Equals(T x, T y) => _previous.Equals(x, y) && _next.Equals(x, y); | |
public int GetHashCode(T obj) => _previous.GetHashCode(obj) * 13 + _next.GetHashCode(obj); | |
} | |
// Base class helpers | |
public abstract class Equatable<T> : IEquatable<T> | |
where T : Equatable<T> | |
{ | |
public sealed override bool Equals(object obj) => Equals(obj as T); | |
public bool Equals(T other) => Equality.Equals((T)this, other); | |
public sealed override int GetHashCode() => Equality.GetHashCode((T)this); | |
protected abstract Equality<T> Equality { get; } | |
} | |
public abstract class Comparable<T> : Equatable<T>, IComparable<T> | |
where T : Comparable<T> | |
{ | |
protected sealed override Equality<T> Equality => Comparer; | |
public int CompareTo(T other) => Comparer.Compare((T)this, other); | |
protected abstract Comparer<T> Comparer { get; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment