| using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
namespace GBricks.Collections
{
public class ComparerBuilder<T>
{
private static readonly ConstantExpression Null = Expression.Constant(null);
private static readonly ConstantExpression Zero = Expression.Constant(0);
private static readonly ConstantExpression One = Expression.Constant(1);
private static readonly ConstantExpression MinusOne = Expression.Constant(-1);
private static readonly ParameterExpression Left = Expression.Parameter(typeof(T), null);
private static readonly ParameterExpression Right = Expression.Parameter(typeof(T), null);
private static readonly ParameterExpression Compare = Expression.Parameter(typeof(int));
private static readonly IEnumerable<ParameterExpression> CompareValiables = Enumerable.Repeat(Compare, 1);
private static readonly LabelTarget Return = Expression.Label(typeof(int));
private static readonly LabelExpression LabelZero = Expression.Label(Return, Zero);
private static readonly GotoExpression ReturnZero = Expression.Return(Return, Zero);
private static readonly GotoExpression ReturnOne = Expression.Return(Return, One);
private static readonly GotoExpression ReturnMinusOne = Expression.Return(Return, MinusOne);
private static readonly GotoExpression ReturnCompare = Expression.Return(Return, Compare);
private static readonly Func<object, object, bool> EqualsDelegate = Object.Equals;
private static readonly Func<int> GetHashCodeDelegate = new object().GetHashCode;
private static readonly Func<int, int, int> RotateRightDelegate = Comparers.RotateRight;
private static readonly bool IsValueType = typeof(T).IsValueType;
public ComparerBuilder() {
EqualsExpressions = new List<Expression>();
GetHashCodeExpressions = new List<Expression>();
CompareExpressions = new List<Expression>();
}
private IList<Expression> EqualsExpressions { get; set; }
private IList<Expression> GetHashCodeExpressions { get; set; }
private IList<Expression> CompareExpressions { get; set; }
#region Expression Helpers
private static BinaryExpression ReferenceEqual(Expression left, Expression right) {
return Expression.ReferenceEqual(left, right);
}
private static BinaryExpression IsNull(Expression value) {
return Expression.ReferenceEqual(value, Null);
}
private static BinaryExpression IsNotNull(Expression value) {
return Expression.ReferenceNotEqual(value, Null);
}
#endregion Expression Helpers
protected virtual Expression MakeEquals<P>(Expression left, Expression right, IEqualityComparer<P> comparer) {
if(left == null) {
throw new ArgumentNullException("left");
} else if(right == null) {
throw new ArgumentNullException("right");
}//if
if(comparer != null) {
// return comparer.Equals(left, right);
Func<P, P, bool> @delegate = comparer.Equals;
var instance = Expression.Constant(comparer);
return Expression.Call(instance, @delegate.Method, left, right);
} else {
if(typeof(P).IsValueType) {
// return left == right;
return Expression.Equal(left, right);
} else {
// return Object.Equals(left, right);
return Expression.Call(EqualsDelegate.Method, left, right);
}//if
}//if
}
protected virtual Expression MakeGetHashCode<P>(Expression value, IEqualityComparer<P> comparer) {
if(value == null) {
throw new ArgumentNullException("value");
}//if
if(comparer != null) {
// return comparer.GetHashCode(left);
Func<P, int> @delegate = comparer.GetHashCode;
var instance = Expression.Constant(comparer);
return Expression.Call(instance, @delegate.Method, value);
} else {
if(typeof(P).IsValueType) {
// return left.GetHashCode();
return Expression.Call(value, GetHashCodeDelegate.Method);
} else {
// return Comparers.GetHashCode(left); [(left == null) ? 0 : left.GetHashCode();]
Func<P, int> @delegate = Comparers.GetHashCode;
return Expression.Call(@delegate.Method, value);
}//if
}//if
}
protected virtual Expression MakeCompare<P>(Expression left, Expression right, IComparer<P> comparer) {
if(left == null) {
throw new ArgumentNullException("left");
} else if(right == null) {
throw new ArgumentNullException("right");
}//if
if(comparer != null) {
// return comparer.Compare(left, right);
Func<P, P, int> @delegate = comparer.Compare;
var instance = Expression.Constant(comparer);
return Expression.Call(instance, @delegate.Method, left, right);
} else {
// return (left < right) ? -1 : (left > right ? 1 : 0);
return Expression.Condition(Expression.LessThan(left, right), MinusOne,
Expression.Condition(Expression.GreaterThan(left, right), One, Zero));
}//if
}
private void AddCore<P>(Tuple<Expression, Expression> args, IEqualityComparer<P> comparer) {
if(args == null) {
throw new ArgumentNullException("args");
}//if
var equals = MakeEquals(args.Item1, args.Item2, comparer);
EqualsExpressions.Add(equals);
var hash = MakeGetHashCode(args.Item1, comparer);
GetHashCodeExpressions.Add(hash);
}
private void AddCore<P>(Tuple<Expression, Expression> args, IComparer<P> comparer) {
if(args == null) {
throw new ArgumentNullException("args");
}//if
var compare = MakeCompare(args.Item1, args.Item2, comparer);
CompareExpressions.Add(compare);
}
private sealed class ReplaceVisitor : ExpressionVisitor
{
public ReplaceVisitor(Expression what, Expression to) {
if(what == null) {
throw new ArgumentNullException("what");
} else if(to == null) {
throw new ArgumentNullException("to");
}//if
What = what;
To = to;
}
public Expression What { get; private set; }
public Expression To { get; private set; }
public override Expression Visit(Expression node) {
if(node == What) {
return To;
}//if
return base.Visit(node);
}
}
private static Tuple<Expression, Expression> Parameters<P>(Expression<Func<T, P>> expression) {
if(expression == null) {
throw new ArgumentNullException("expression");
} else if(expression.Parameters.Count != 1) {
throw new ArgumentException("expression.Parameters.Count != 1", "expression");
}//if
var left = new ReplaceVisitor(expression.Parameters[0], Left);
var right = new ReplaceVisitor(expression.Parameters[0], Right);
return Tuple.Create(left.Visit(expression.Body), right.Visit(expression.Body));
}
public ComparerBuilder<T> Add<P>(Expression<Func<T, P>> expression, IEqualityComparer<P> equality, IComparer<P> comparison) {
var args = Parameters(expression);
AddCore(args, equality);
AddCore(args, comparison);
return this;
}
public ComparerBuilder<T> Add<P, C>(Expression<Func<T, P>> expression, C comparer) where C : IEqualityComparer<P>, IComparer<P> {
return Add(expression, comparer, comparer);
}
public ComparerBuilder<T> AddEquality<P>(Expression<Func<T, P>> expression, IEqualityComparer<P> equality = null) {
var args = Parameters(expression);
AddCore(args, equality);
return this;
}
public ComparerBuilder<T> AddComparison<P>(Expression<Func<T, P>> expression, IComparer<P> comparison = null) {
var args = Parameters(expression);
AddCore(args, comparison);
return this;
}
public ComparerBuilder<T> AddDefault<P>(Expression<Func<T, P>> expression) {
return Add(expression, EqualityComparer<P>.Default, Comparer<P>.Default);
}
public ComparerBuilder<T> Add<P>(Expression<Func<T, P>> expression) {
return Add(expression, null, null);
}
private static Expression<Func<T, T, bool>> BuildEquals(IEnumerable<Expression> items) {
if(items == null) {
throw new ArgumentNullException("items");
}//if
var expression = items.Aggregate(Expression.AndAlso);
var body = IsValueType
? expression
// return (object)x == (object)y || ((object)x != null && (object)y != null && expression);
: Expression.OrElse(
ReferenceEqual(Left, Right),
Expression.AndAlso(
Expression.AndAlso(IsNotNull(Left), IsNotNull(Right)),
expression));
return Expression.Lambda<Func<T, T, bool>>(body, Left, Right);
}
private static Expression<Func<T, int>> BuildGetHashCode(IEnumerable<Expression> items) {
if(items == null) {
throw new ArgumentNullException("items");
}//if
var expression = items.Skip(1).Select((item, index) => Tuple.Create(item, index + 1))
.Aggregate(items.First(), (acc, item) =>
Expression.ExclusiveOr(acc,
Expression.Call(RotateRightDelegate.Method, item.Item1, Expression.Constant(item.Item2))));
var body = IsValueType
? expression
// return ((object)x == null) ? 0 : expression;
: Expression.Condition(IsNull(Left), Zero, expression);
return Expression.Lambda<Func<T, int>>(body, Left);
}
private static Expression<Func<T, T, int>> BuildCompare(IEnumerable<Expression> items) {
if(items == null) {
throw new ArgumentNullException("items");
}//if
var reverse = items.Reverse();
Expression seed = Expression.Return(Return, reverse.First());
var expression = reverse.Skip(1).Aggregate(seed,
(acc, value) => Expression.IfThenElse(
Expression.NotEqual(Expression.Assign(Compare, value), Zero), ReturnCompare, acc));
var body = IsValueType
? expression
//if((object)x == (object)y) {
// return 0;
//} else if((object)x == null) {
// return -1;
//} else if((object)y == null) {
// return 1;
//} else {
// return expression;
//}//if
: Expression.IfThenElse(ReferenceEqual(Left, Right), ReturnZero,
Expression.IfThenElse(IsNull(Left), ReturnMinusOne,
Expression.IfThenElse(IsNull(Right), ReturnOne, expression)));
var block = Expression.Block(CompareValiables, body, LabelZero);
return Expression.Lambda<Func<T, T, int>>(block, Left, Right);
}
public EqualityComparer<T> ToEqualityComparer() {
if(EqualsExpressions.Count == 0 || GetHashCodeExpressions.Count == 0) {
return Comparers.EmptyEqualityComparer<T>();
}//if
Debug.Assert(EqualsExpressions.Count == GetHashCodeExpressions.Count);
var equals = BuildEquals(EqualsExpressions);
var hash = BuildGetHashCode(GetHashCodeExpressions);
return Comparers.Create(equals.Compile(), hash.Compile());
}
public Comparer<T> ToComparer() {
if(CompareExpressions.Count == 0) {
return Comparers.EmptyComparer<T>();
}//if
var compare = BuildCompare(CompareExpressions);
return Comparers.Create(compare.Compile());
}
}
}
Используется класс c методами-помощниками Comparers:
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace GBricks.Collections
{
public static class Comparers
{
public static EqualityComparer<T> EmptyEqualityComparer<T>() {
return ConstEqualityComparer<T>.Default;
}
public static Comparer<T> EmptyComparer<T>() {
return ConstComparer<T>.Default;
}
public static EqualityComparer<T> Const<T>(bool equals, int hash) {
return new ConstEqualityComparer<T>(equals, hash);
}
public static Comparer<T> Const<T>(int compare) {
return new ConstComparer<T>(compare);
}
public static EqualityComparer<T> Create<T>(Func<T, T, bool> equals, Func<T, int> hash) {
if(equals == null) {
throw new ArgumentNullException("equals");
}//if
return new MethodEqualityComparer<T>(equals, hash);
}
public static EqualityComparer<T> Create<T>(Func<T, T, bool> equals) {
return Create(equals, item => (item == null) ? 0 : item.GetHashCode());
}
public static Comparer<T> Create<T>(Func<T, T, int> compare) {
if(compare == null) {
throw new ArgumentNullException("compare");
}//if
return new MethodComparer<T>(compare);
}
public static int RotateRight(int value, int places) {
if((places &= 0x1F) == 0) {
return value;
}//if
var mask = ~0x7FFFFFFF >> (places - 1);
return ((value >> places) & ~mask) | ((value << (32 - places)) & mask);
}
public static int GetHashCode<X>(X value) {
if(value == null) {
return 0;
}//if
return value.GetHashCode();
}
[Serializable]
private sealed class MethodEqualityComparer<T> : EqualityComparer<T>
{
public MethodEqualityComparer(Func<T, T, bool> equals, Func<T, int> hash) {
if(equals == null) {
throw new ArgumentNullException("equals");
} else if(hash == null) {
throw new ArgumentNullException("hash");
}//if
EqualsMethod = equals;
GetHashCodeMethod = hash;
}
private Func<T, T, bool> EqualsMethod { get; set; }
private Func<T, int> GetHashCodeMethod { get; set; }
public override bool Equals(T x, T y) {
return EqualsMethod(x, y);
}
public override int GetHashCode(T obj) {
return GetHashCodeMethod(obj);
}
}
[Serializable]
private sealed class MethodComparer<T> : Comparer<T>
{
public MethodComparer(Func<T, T, int> compare) {
if(compare == null) {
throw new ArgumentNullException("compare");
}//if
CompareMethod = compare;
}
private Func<T, T, int> CompareMethod { get; set; }
public override int Compare(T x, T y) {
return CompareMethod(x, y);
}
}
[Serializable]
private sealed class ConstEqualityComparer<T> : EqualityComparer<T>
{
private static readonly EqualityComparer<T> @default = new ConstEqualityComparer<T>(true, 0);
public ConstEqualityComparer(bool equals, int hash) {
EqualsValue = equals;
GetHashCodeValue = hash;
}
public static new EqualityComparer<T> Default {
[DebuggerStepThrough]
get { return @default; }
}
private bool EqualsValue { get; set; }
private int GetHashCodeValue { get; set; }
public override bool Equals(T x, T y) {
return EqualsValue;
}
public override int GetHashCode(T obj) {
return GetHashCodeValue;
}
}
[Serializable]
private sealed class ConstComparer<T> : Comparer<T>
{
private static readonly Comparer<T> @default = new ConstComparer<T>(0);
public ConstComparer(int compare) {
CompareValue = compare;
}
public static new Comparer<T> Default {
[DebuggerStepThrough]
get { return @default; }
}
private int CompareValue { get; set; }
public override int Compare(T x, T y) {
return CompareValue;
}
}
}
}
|