Árboles de expresiones, comparador de objetos.

c# expression-trees

Pregunta

Necesito generar un árbol de expresiones que verifique la igualdad de dos objetos (argumentos). Sé que estos objetos tendrán propiedades, así que tengo que comparar sus valores, ¿cómo hacer esto? Así que tengo algo como obj1 , obj2 y un conjunto de cadenas con nombres de propiedades que necesito verificar. Así es como veo esto:

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])
        )
    );

¿Cómo implementar el acceso a las propiedades una por una en Loop?

PS E es mi alias para Expression .

Respuesta aceptada

Debería usar la reflexión para descubrir las propiedades que desea y luego, básicamente, crear una gran serie de expresiones AndAlso. p.ej

    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();
    }

El código anterior solo funciona en clases, crea una expresión que es aproximadamente

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

No es una solución completa, ya que asume que desea comparar todas las propiedades y que todas sus propiedades son legibles, pero debería ponerlo en la ruta. También es compatible con .NET 3.5 si te interesan tales cosas.




Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow