2012-10-02 108 views
0

我有以下方法來比較DTO。使用表達式比較屬性和子屬性上的對象

bool Equals<T1, T2>(T1 t1, T2 t2, params Expression<Func<T1, object>>[] accessors) 
{ 
    return !(
    from accessor in accessors 
    select ((MemberExpression) accessor.Body).Member.Name into propertyName 
    let p1 = typeof (T1).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) 
    let p2 = typeof (T2).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) 
    let p1val = p1.GetValue(t1, null) 
    let p2val = p2.GetValue(t2, null) 
    where !Equals(p1val, p2val) 
    select p1val 
).Any(); 
} 

我可以使用調用這個(ab是對象的情況下,按照慣例共享相同的屬性,但不是相同的對象):

Equals(a, b, x => x.PropertyOne, x => x.PropertyTwo); 

哪些對象進行比較財產財產,這在大多數情況下都很好。

但是,我發現一個案例,我需要比較具有複雜類型屬性的對象,以及我想要在複雜類型而不是對象上比較屬性的對象。事情是這樣的:

Equals(a, b, x => x.ComplexTypeProperty.ChildProp); 

我已經意識到我需要離開舒適的反映比較和輸入表達式的土地,但這裏的主要任務是能夠表達兩個屬性訪問和屬性訪問通過複雜的類型屬性,這就是我迷失的地方。

任何指針都會很好,謝謝!

回答

1

的任務並不複雜:

  1. 確定由表達式給出屬性路徑或表達式。比如這個擴展方法會給你這樣的:

    public static IEnumerable<string> GetPropertiesNames<T, G>(this Expression<Func<T, G>> pathExpression) 
    { 
        List<string> _propertyNames = new List<string>(); 
    
        Expression expression = pathExpression.Body; 
    
        if (expression.NodeType == ExpressionType.Convert) 
        { 
         var convert = (UnaryExpression)pathExpression.Body; 
         expression = convert.Operand; 
        } 
    
        while (expression.NodeType == ExpressionType.MemberAccess) 
        { 
         MemberExpression memberExpression = (MemberExpression)expression; 
          if(!(memberExpression.Member is PropertyInfo)) 
           throw new InvalidOperationException(); 
         _propertyNames.Add(memberExpression.Member.Name); 
         expression = memberExpression.Expression; 
        } 
    
        if (expression.NodeType != ExpressionType.Parameter) 
         throw new InvalidOperationException(); 
    
        return _propertyNames; 
    } 
    
  2. 聚集表達式爲第二類,以創建函數將返回值:即檢索值和比較

    var parameter = Expression.Parameter(typeof(T2));  
    var expressionToConvert = accessors[0]; //for future loop 
    
        var propertyChainDescriptor = expressionToConvert.GetPropertiesNames() 
         .Aggregate(new { Expression = (Expression)parameterCasted, Type = typeof(T2)}, 
          (current, propertyName) => 
          { 
           var property = current.Type.GetProperty(propertyName); 
           var expression = Expression.Property(current.Expression, property); 
           return new { Expression = (Expression)expression, Type = property.PropertyType }; 
          }); 
    
        var body = propertyChainDescriptor.Expression; 
    
        if (propertyChainDescriptor.Type.IsValueType) 
        { 
         body = Expression.Convert(body, typeof(object)); 
        } 
    
        var t2PropertyRetriver = Expression.Lambda<Func<T2, object>>(body, parameter).Compile(); 
    
  3. 現在執行方法:

    var t1PropertyRetriver = accessor[0].Compile(); 
        var t1Value = t1PropertyRetriver(t1); 
        var t2Value = t2PropertyRetriver(t2); 
    
        var areEqual = object.Equals(t1Value,t2Value); 
    

好主意是添加一些緩存生成的方法,因爲編譯過程很昂貴。