C# 使用表达式比较属性和子属性上的对象

C# 使用表达式比较属性和子属性上的对象,c#,expression-trees,expression,C#,Expression Trees,Expression,我有以下方法来比较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 =

我有以下方法来比较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();
}
它按属性比较对象属性,这在大多数情况下都很好

但是,我发现了这样一种情况:我需要比较具有复杂类型属性的对象,并且我希望比较复杂类型而不是对象的属性。大概是这样的:

Equals(a, b, x => x.ComplexTypeProperty.ChildProp );
我已经意识到,我需要离开舒适的反射比较,进入Expression land,但这里的主要任务是能够通过复杂类型的属性来表达属性访问器和属性访问器,这就是我丢失的地方


任何指示都很好,谢谢

任务没有那么复杂:

  • 确定由表达式给出的属性路径或表达式。例如,此扩展方法将为您提供以下内容:

    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;
    }
    
  • 好主意是为生成的方法添加一些缓存,因为编译过程很昂贵

    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;
    }
    
    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();
    
        var t1PropertyRetriver = accessor[0].Compile();
        var t1Value = t1PropertyRetriver(t1);
        var t2Value = t2PropertyRetriver(t2);
    
        var areEqual = object.Equals(t1Value,t2Value);