2

I was looking at another question that stated how Expression can be significantly faster than reflection since it can be precompiled to IL.

I'm not really sure how to use it though. Here is some code used in a base class for a Value Oject (in the DDD sense) where the basic idea is to use the values of all public properties to determine equality, which it gets via reflection. By using this base class, you needn't implement equality for subclasses that have Value Object.

protected virtual bool HasSameObjectSignatureAs(BaseObject compareTo) 
{
var signatureProperties = GetType().GetProperties();
foreach (var property in signatureProperties)
{
    var valueOfThisObject = property.GetValue(this, null);
    var valueOfCompareTo = property.GetValue(compareTo, null);

    if (valueOfThisObject == null && valueOfCompareTo == null) {
        continue;
    }

    if ((valueOfThisObject == null ^ valueOfCompareTo == null) ||
        (!valueOfThisObject.Equals(valueOfCompareTo))) {
            return false;
    }
}

How would this code be re-written using Expression?

Cheers,
Berryl

Community
  • 1
  • 1
Berryl
  • 12,471
  • 22
  • 98
  • 182
  • Kindly give descriptive titles to questions. Btw, its not precompiled, its postcompiled, except you can cache and reuse the code. – nawfal Dec 20 '13 at 05:55

1 Answers1

4

You can do this by building up a nested And expression for each property you want to compare:

protected Expression<Func<BaseObject, bool>> CreatePropertiesEqualExpression(BaseObject other)
{
    if (! other.GetType().IsSubclassOf(this.GetType())) throw new ArgumentException();

    var properties = this.GetType().GetProperties();
    Expression trueExpr = Expression.Constant(true);
    Expression thisExpr = Expression.Constant(this);
    ParameterExpression paramExpr = Expression.Parameter(typeof(BaseObject), "compareTo");
    Expression downCastExpr = Expression.Convert(paramExpr, other.GetType());

    MethodInfo eqMethod = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static);

    Expression propCompExpr = properties.Aggregate(trueExpr, (expr, prop) =>
    {
        Expression thisPropExpr = Expression.Property(thisExpr, prop);
        Expression compPropExpr = Expression.Property(downCastExpr, prop);
        Expression eqExpr = Expression.Call(null, eqMethod, Expression.Convert(thisPropExpr, typeof(object)), Expression.Convert(compPropExpr, typeof(object)));

        return Expression.And(expr, eqExpr);
    });

    return Expression.Lambda<Func<BaseObject, bool>>(propCompExpr, paramExpr);
}

You can then use it like this:

public class SubObject : BaseObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    private Func<BaseObject, bool> eqFunc;

    public bool IsEqualTo(SubObject other)
    {
        if(this.eqFunc == null)
        {
            var compExpr = this.CreatePropertiesEqualExpression(other);
            this.eqFunc = compExpr.Compile();
        }
        return this.eqFunc(other);
    }
}
Lee
  • 142,018
  • 20
  • 234
  • 287
  • Would it be more performant that reflection even without the cache? When would you need to invalidate the cache? Could you show code with the cache? Cheers – Berryl Sep 25 '12 at 13:31
  • @Berryl - I've updated the code to cache the comparison delegate. Creating and compiling the expression each time will be slower than your current approach, but if you call this method a lot on a single instance, it may be quicker to use a compiled delegate in the long run. I don't know how you're using this method, but it would be more efficient if you created a static delegate per `BaseObject` type, and changed the comparison type to `Func` where T is the specific subtype. – Lee Sep 25 '12 at 13:43