2012-10-02 1 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. 는 표현에 의해 주어진 속성 경로 또는 식을 결정합니다. 값을 검색하고 비교

    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(); 
    
  2. 지금 실행 방법 :

    값을 반환합니다 기능을 만들 수있는 두 번째 유형에 대한

    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; 
    } 
    
  3. 집계 표현 : 예를 들어이 확장 방법은 당신이를 줄 것이다

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

좋은 생각은 일부 캐싱을 추가하는 것입니다. 컴파일 프로세스가 비싸기 때문에 생성 된 메소드.