2012-10-02 52 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); 
    

好主意是添加一些缓存生成的方法,因为编译过程很昂贵。