2010-12-07 2 views
1

Мне нужно сгенерировать дерево выражений, чем проверять два объекта (аргументы) для равенства. Я знаю, что эти объекты будут иметь свойства, поэтому мне нужно сравнить их значения, как это сделать? Итак, у меня есть что-то вроде obj1, obj2 и массив строк с именами свойств, которые мне нужно проверить. Вот как я вижу это:Деревья выражений, сопоставление объектов

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

Как осуществить доступ к свойствам один за другим в цикле?

P.S. E - мой псевдоним для Expression.

ответ

2

Вы должны использовать отражение, чтобы обнаружить свойства, которые хотите, а затем в основном создать одну большую серию выражений AndAlso. , например.

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

Приведенные выше код работает только на классах, создает выражение, которое грубо

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

Это не полное решения, поскольку это предполагает, что вы хотите сравнить все свойства и все ваши свойства читаемые, но это должно заставить вас идти по пути. Это также совместимо с .NET 3.5, если вы заботитесь о таких вещах.

1

Вы можете сделать это с помощью отражения.

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

Если вы используете приведенный выше подход, не забудьте передать строки в строки, а не объекты.

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) 
); 
+0

Спасибо.Я уже сделал это с помощью отражения, но это очень медленно, поэтому я решил сгенерировать методы сравнения для разных объектов и сохранить их скомпилированные в памяти, чем проверить, существует ли метод сравнения для заданного типа и просто позвоните. Это в сто раз быстрее, чем отражение, потому что выражение скомпилировано один раз для каждого типа, который мне нужно сравнить. – 2010-12-07 19:33:18

Смежные вопросы