Skip to content

Instantly share code, notes, and snippets.

@danielwertheim
Created May 11, 2012 07:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielwertheim/2658225 to your computer and use it in GitHub Desktop.
Save danielwertheim/2658225 to your computer and use it in GitHub Desktop.
ExpressionComparer does not support AnonymousTypes
private static LambdaExpression ExpressionFactory(int customerNoFrom, int customerNoTo)
{
Func<Expression<Func<Customer, bool>>, LambdaExpression> fn = expression => expression;
return fn(c =>
c.CustomerNo >= customerNoFrom && c.CustomerNo <= customerNoTo &&
c.DeliveryAddress.Street == "The delivery street #544");
}
static void Main(string[] args)
{
var e1 = ExpressionFactory(500, 550);
var e2 = ExpressionFactory(500, 550);
var r = ExpressionComparer.AreEqual(e1, e2); //Gives false
}
//In ExpressionComparer
protected virtual bool CompareConstant(ConstantExpression a, ConstantExpression b)
{
if (this.fnCompare != null)
{
return this.fnCompare(a.Value, b.Value);
}
else
{
return object.Equals(a.Value, b.Value) || CompareAnonymous(a.Type, a.Value, b.Value); //ADDED
}
}
protected virtual bool CompareAnonymous(Type t, object a, object b)
{
//TODO: Add check if anonymous type before passing on call
return TypeCompares.Get(t).AreEqual(a, b);
}
//NEEDS TO BE EXTENDED WITH TRAVERSAL OF ENUMERABLES ETC!!!
public static class TypeCompares
{
private static readonly ConcurrentDictionary<Type, TypeCompare> Comparers = new ConcurrentDictionary<Type, TypeCompare>();
public static TypeCompare Get(Type t)
{
return Comparers.GetOrAdd(t, new TypeCompare(t));
}
}
public class TypeCompare
{
private readonly DynamicFieldGetter[] PublicFields;
public TypeCompare(Type type)
{
PublicFields = type.GetFields().Select(DynamicFieldFactory.GetterFor).ToArray();
}
public bool AreEqual(object x, object y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null && y == null)
return true;
if (x == null)
return false;
if (y == null)
return false;
foreach (var field in PublicFields)
{
var xv = field.GetValue(x);
var yv = field.GetValue(y);
if (!object.Equals(xv, yv))
return false;
}
return true;
}
}
public class DynamicFieldGetter
{
private readonly Func<object, object> _accessor;
public DynamicFieldGetter(Func<object, object> accessor)
{
_accessor = accessor;
}
public object GetValue<T>(T item)
{
return _accessor(item);
}
}
public static class DynamicFieldFactory
{
private static readonly Type ObjectType = typeof(object);
private static readonly Type IlGetterType = typeof(Func<object, object>);
public static DynamicFieldGetter GetterFor(FieldInfo p)
{
if (p.DeclaringType.IsKeyValuePairType())
return new DynamicFieldGetter(CreateLambdaGetter(p.DeclaringType, p));
return new DynamicFieldGetter(CreateIlGetter(p));
}
private static Func<object, object> CreateLambdaGetter(Type type, FieldInfo property)
{
var objExpr = Expression.Parameter(ObjectType, "theItem");
var castedObjExpr = Expression.Convert(objExpr, type);
var p = Expression.Field(castedObjExpr, property);
var castedProp = Expression.Convert(p, ObjectType);
var lambda = Expression.Lambda<Func<object, object>>(castedProp, objExpr);
return lambda.Compile();
}
private static Func<object, object> CreateIlGetter(FieldInfo propertyInfo)
{
var getter = CreateDynamicGetMethod(propertyInfo);
var generator = getter.GetILGenerator();
generator.DeclareLocal(ObjectType);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
generator.Emit(OpCodes.Ldfld, propertyInfo);
if (!propertyInfo.FieldType.IsClass)
generator.Emit(OpCodes.Box, propertyInfo.FieldType);
generator.Emit(OpCodes.Ret);
return (Func<object, object>)getter.CreateDelegate(IlGetterType);
}
private static DynamicMethod CreateDynamicGetMethod(FieldInfo propertyInfo)
{
var args = new[] { ObjectType };
var name = string.Format("_{0}{1}_", "Get", propertyInfo.Name);
var returnType = ObjectType;
return !propertyInfo.DeclaringType.IsInterface
? new DynamicMethod(
name,
returnType,
args,
propertyInfo.DeclaringType,
true)
: new DynamicMethod(
name,
returnType,
args,
propertyInfo.Module,
true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment