Skip to content

Instantly share code, notes, and snippets.

@RichardD2
Created June 17, 2015 15:17
Show Gist options
  • Save RichardD2/7e14217426d999db3d21 to your computer and use it in GitHub Desktop.
Save RichardD2/7e14217426d999db3d21 to your computer and use it in GitHub Desktop.
Extension methods for working with IComparer<T> classes.
using System;
using System.Collections.Generic;
namespace NBS.Core.Collections.Generic
{
public static class ComparerExtensions
{
private sealed class ReverseComparer<T> : IComparer<T>
{
public readonly IComparer<T> Original;
public ReverseComparer(IComparer<T> original)
{
Original = original;
}
public int Compare(T x, T y)
{
// NB: Reverse the order of the comparands;
// DO NOT negate the result of the comparison.
// If the original comparer returns int.MinValue,
// negating the result will have no effect.
// (-int.MinValue === int.MinValue)
return Original.Compare(y, x);
}
}
public static IComparer<T> Reverse<T>(this IComparer<T> comparer)
{
if (comparer == null) throw new ArgumentNullException("comparer");
var reversed = comparer as ReverseComparer<T>;
if (reversed != null) return reversed.Original;
return new ReverseComparer<T>(comparer);
}
private sealed class ComparisonComparer<T> : IComparer<T>
{
private readonly Comparison<T> _comparison;
private readonly bool _descending;
public ComparisonComparer(Comparison<T> comparison, bool descending)
{
_comparison = comparison;
_descending = descending;
}
public int Compare(T x, T y)
{
// NB: Reverse the order of the comparands;
// DO NOT negate the result of the comparison.
// If the original comparer returns int.MinValue,
// negating the result will have no effect.
// (-int.MinValue === int.MinValue)
return _descending ? _comparison(y, x) : _comparison(x, y);
}
}
public static IComparer<T> ToComparer<T>(this Comparison<T> comparison, bool descending = false)
{
if (comparison == null) throw new ArgumentNullException("comparison");
return new ComparisonComparer<T>(comparison, descending);
}
private sealed class ExpressionComparer<TValue, TKey> : IComparer<TValue>
{
private readonly IComparer<TKey> _keyComparer;
private readonly Func<TValue, TKey> _expression;
private readonly bool _descending;
public ExpressionComparer(Func<TValue, TKey> expression, bool descending, IComparer<TKey> keyComparer)
{
if (keyComparer == null) keyComparer = Comparer<TKey>.Default;
_expression = expression;
_keyComparer = keyComparer;
_descending = descending;
}
public int Compare(TValue x, TValue y)
{
// NB: Reverse the order of the comparands;
// DO NOT negate the result of the comparison.
// If the original comparer returns int.MinValue,
// negating the result will have no effect.
// (-int.MinValue === int.MinValue)
return _descending ? CompareCore(y, x) : CompareCore(x, y);
}
private int CompareCore(TValue x, TValue y)
{
if (x == null) return y == null ? 0 : -1;
if (y == null) return 1;
return _keyComparer.Compare(_expression(x), _expression(y));
}
}
public static IComparer<TValue> ToComparer<TValue, TKey>(this Func<TValue, TKey> expression, bool descending = false, IComparer<TKey> keyComparer = null)
{
if (expression == null) throw new ArgumentNullException("expression");
return new ExpressionComparer<TValue, TKey>(expression, descending, keyComparer);
}
private sealed class CompoundComparer<T> : IComparer<T>
{
private readonly IComparer<T> _first;
private readonly IComparer<T> _second;
public CompoundComparer(IComparer<T> first, IComparer<T> second)
{
_first = first;
_second = second;
}
public int Compare(T x, T y)
{
int result = _first.Compare(x, y);
if (result == 0) result = _second.Compare(x, y);
return result;
}
}
public static IComparer<T> ThenBy<T>(this IComparer<T> first, IComparer<T> second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
return new CompoundComparer<T>(first, second);
}
public static IComparer<T> ThenByDescending<T>(this IComparer<T> first, IComparer<T> second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
return first.ThenBy(second.Reverse());
}
public static IComparer<T> ThenBy<T>(this IComparer<T> first, Comparison<T> comparison)
{
if (first == null) throw new ArgumentNullException("first");
if (comparison == null) throw new ArgumentNullException("comparison");
return first.ThenBy(comparison.ToComparer(false));
}
public static IComparer<T> ThenByDescending<T>(this IComparer<T> first, Comparison<T> comparison)
{
if (first == null) throw new ArgumentNullException("first");
if (comparison == null) throw new ArgumentNullException("comparison");
return first.ThenBy(comparison.ToComparer(true));
}
public static IComparer<T> ThenBy<T, TKey>(this IComparer<T> first, Func<T, TKey> expression, IComparer<TKey> keyComparer = null)
{
if (first == null) throw new ArgumentNullException("first");
if (expression == null) throw new ArgumentNullException("expression");
return first.ThenBy(expression.ToComparer(false, keyComparer));
}
public static IComparer<T> ThenByDescending<T, TKey>(this IComparer<T> first, Func<T, TKey> expression, IComparer<TKey> keyComparer = null)
{
if (first == null) throw new ArgumentNullException("first");
if (expression == null) throw new ArgumentNullException("expression");
return first.ThenBy(expression.ToComparer(true, keyComparer));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment