2010-12-07 86 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

Thanks.I've已經在使用反射做它,但它是非常該死的慢,所以我決定了生成不同的比較對象的方法,並將其儲存在內存中編譯,比我檢查是否爲預設的類別比較法存在,只是叫它。它比反射快上百倍,因爲表達式是我需要比較的每種類型編譯一次的。 – 2010-12-07 19:33:18