1

I need to generate expression tree than checks two objects (arguments) for equality. I know that these objects will have properties, so I have to compare their values, how to do this? So i have something like obj1, obj2 and array of strings with property names i need to check. Here's how i see this :

var leftObject = E.Parameter(typeof (object), "leftObject");
var rightObject = E.Parameter(typeof (object), "rightObject");
var properties = E.Parameter(typeof (string[]), "properties");
var i = E.Parameter(typeof(int), "i");
var equal = E.Parameter(typeof (bool), "equal");

var body = E.Block
    (
        new[] { properties, i},
        E.Assign(properties,E.Constant(props)),
        E.Assign(i,E.Constant(0)),
        E.Assign(equal,E.Constant(true)),

        E.Loop
        (
            E.Property(leftObject,props[i]) == E.Property(rightObject,props[i])
        )
    );

How to implement accessing to properties one by one in Loop?

P.S. E is my alias for Expression.

nawfal
  • 70,104
  • 56
  • 326
  • 368
illegal-immigrant
  • 8,089
  • 9
  • 51
  • 84

3 Answers3

3

Perhaps like this: Hows to quick check if data transfer two objects have equal properties in C#?

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

You should use reflection to discover the properties you want then basically create one big series of AndAlso expressions. e.g.

    public static Func<T, T, bool> BuildStructuralComparerDelegate<T>() where T:class
    {
        var left = Expression.Parameter(typeof(T), "left");
        var right = Expression.Parameter(typeof(T), "right");
        var referenceEquals = typeof(object).GetMethod("ReferenceEquals");
        Expression expression = Expression.AndAlso(
            Expression.Not(
                Expression.Call(
                    null,
                    referenceEquals,
                    left,
                    Expression.Default(typeof(T))
                )
            ),
            Expression.Not(
                null,
                Expression.Call(
                    referenceEquals,
                    right,
                    Expression.Default(typeof(T))
                )
            )
        );
        Array.ForEach(typeof(T).GetProperties(),property =>
            expression = Expression.AndAlso(
                expression,
                Expression.Equal(
                    Expression.Property(left, property),
                    Expression.Property(right, property)
                )
            )
        );
        var lambdaExp = Expression.Lambda<Func<T, T, bool>>(
            Expression.OrElse(
                Expression.Call(
                    null,
                    referenceEquals,
                    left,
                    right
                ),
                expression
            ),
            left,
            right
        );
        return lambdaExp.Compile();
    }

The above code only works on classes, creates an expression that is roughly

(left,right)=> object.ReferenceEquals(left,right) || (left != null && right != null && left.Property1 == right.Property1 && left.Property2 == right.Property2 && ... && left.PropertyN == right.PropertyN);

It's not a complete solution as it assumes you want to compare all properties and all your properties are Readable, but it should get you on the path. It's also .NET 3.5 compatible if you care about such things.

Michael B
  • 7,512
  • 3
  • 31
  • 57
1

You can do this using reflection.

bool eq = true;
foreach (string prop in PropertiesYouWantToCheck){
    PropertyInfo propInfo = obj1.GetType().GetProperties().Single(x => x.Name == prop);
    if(propInfo.GetValue(obj1, null) != propInfo.GetValue(obj2, null)){
        eq = false;
        break;
    }
}

If you use the above approach make sure to cast strings to strings not objects.

 string[] props = null;
 var leftObject = Expression.Parameter(typeof(object), "leftObject");
 var rightObject = Expression.Parameter(typeof(object), "rightObject");
 var equal = Expression.Variable(typeof(bool), "equal");
 var lbl = Expression.Label();
 var returnTarget = Expression.Label();

 var body = Expression.Block
  (
   typeof(bool),
   equal,
   Expression.Assign(equal, Expression.Constant(true)),

   Expression.Block(
    props.Select(property =>
      Expression.IfThen(
       Expression.NotEqual(Expression.Property(leftObject, property),
            Expression.Property(rightObject, property)),
       Expression.Block(
        Expression.Assign(equal, Expression.Constant(false)),
        Expression.Goto(lbl)
       )
      )
     )
   ),

   Expression.Label(lbl),
   Expression.Return(returnTarget, equal, typeof(bool)),
   Expression.Label(returnTarget)
  );
gh9
  • 10,169
  • 10
  • 63
  • 96
andrewjsaid
  • 489
  • 3
  • 11
  • Thanks.I've already done it using reflection, but it's very damn slow, so i decided to generate compare-methods for different objects and store them compiled in memory, than i check if compare-method for preset type exists and just call it. It is hundred times faster than reflection, because expression is compiled one time per type i need to compare. – illegal-immigrant Dec 07 '10 at 19:33