2013-05-08 1 views
8

동적으로 값을 읽는 개체의 모든 속성에 대해 람다 식을 만들려고합니다. 내가 지금까지 무엇을 가지고표현 트리를 사용하여 개체의 속성 읽기

: 내가 "TypeOfProperty"만큼 functionThatGetsValue를 호출 할 때

var properties = typeof (TType).GetProperties().Where(p => p.CanRead); 

foreach (var propertyInfo in properties) 
{ 
    var getterMethodInfo = propertyInfo.GetGetMethod(); 

    var entity = Expression.Parameter(typeof (TType)); 

    var getterCall = Expression.Call(entity, getterMethodInfo); 

    var lambda = Expression.Lambda(getterCall, entity); 
    var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda; 
    var functionThatGetsValue = expression.Compile(); 
} 

코드는 잘 작동은 하드입니다. "TypeOfPoperty"를 동적으로 전달할 수 없다는 것을 알고 있습니다. 내 목표를 달성하려면 어떻게해야합니까?

+0

목표는 무엇입니까? 당신은 람다 식을 만들고 싶다고 말합니다. 컴파일 된 델리게이트 ('functionThatGetsValue') 만 필요합니까, 아니면 중간 표현식 트리 ('expression')도 필요합니까? – LukeH

+0

@LukeH, 그냥 컴파일 된 대리자. 감사. (목표는 객체 목록을 반복하여 속성에서 모든 값을 읽는 것입니다. 약간의 성능을 얻으려면 리플렉션을 사용하는 대신이 방법으로 수행하고 싶습니다.) – gsharp

+1

비슷한 결과를 얻으려고하면 Func 를 반환하고 호출자 측의 특정 속성 유형으로 반환 값을 캐스팅합니다. –

답변

7

(위 의견에 따라)를 Func<TType, object> 위임에 만족한다고 가정하면, 당신은 그것을 달성하기 위해 Expression.Convert를 사용할 수 있습니다

var properties = typeof(TType).GetProperties().Where(p => p.CanRead); 

foreach (var propertyInfo in properties) 
{ 
    MethodInfo getterMethodInfo = propertyInfo.GetGetMethod(); 
    ParameterExpression entity = Expression.Parameter(typeof(TType)); 
    MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo); 

    UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object)); 
    LambdaExpression lambda = Expression.Lambda(castToObject, entity); 

    var functionThatGetsValue = (Func<TType, object>)lambda.Compile(); 
} 
5

을 대답 here을 발견 인터넷 검색 시간 후. 실제로 값을 직접 설정에 이상 gsharp의 게시물을 수정 한

public static class PropertyInfoExtensions 
{ 
    public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var property = Expression.Property(instance, propertyInfo); 
     var convert = Expression.TypeAs(property, typeof(object)); 
     return (Func<T, object>)Expression.Lambda(convert, instance).Compile(); 
    } 

    public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var argument = Expression.Parameter(typeof(object), "a"); 
     var setterCall = Expression.Call(
      instance, 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 
     return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile(); 
    } 
} 
0

하고 사용하기가 좀 더 쉽게 만들 : 그것은 다른 사람이 같은 문제를 가지고 도움이 될 나는이 블로그 게시물에서 조각을 추가했습니다. DynamicCast 기능이 도입되어 사용자의 유형을 알 필요가 있으므로 이상적이지 않습니다. 내 목표는 우리를 강하게 입력하고 객체를 반환하지 않고 동적 키워드를 사용하지 않으려 고 노력하는 것이 었습니다. 또한 "마법"을 최소한으로 유지하십시오.

public static T DynamicCast<T>(this object value) 
    { 
     return (T) value; 
    } 
    public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance) 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var property = Expression.Property(instance, propertyInfo); 
     var convert = Expression.TypeAs(property, propertyInfo.PropertyType); 
     var lambda = Expression.Lambda(convert, instance).Compile(); 
     var result = lambda.DynamicInvoke(objectInstance); 


     return result; 
    } 

    public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value) 
     where T : class 
     where TP : class 
    { 
     if (typeof(T) != propertyInfo.DeclaringType) 
     { 
      throw new ArgumentException(); 
     } 

     var instance = Expression.Parameter(propertyInfo.DeclaringType, "i"); 
     var argument = Expression.Parameter(propertyInfo.PropertyType, "a"); 
     var setterCall = Expression.Call(
      instance, 
      propertyInfo.GetSetMethod(), 
      Expression.Convert(argument, propertyInfo.PropertyType)); 

     var lambda = Expression.Lambda(setterCall, instance, argument).Compile(); 
     lambda.DynamicInvoke(objectInstance, value); 
    } 

예 :

 public void Get_Value_Of_Property() 
    { 
     var testObject = new ReflectedType 
     { 
      AReferenceType_No_Attributes = new object(), 
      Int32WithRange1_10 = 5, 
      String_Requires = "Test String" 
     }; 

     var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>(); 

     result.Should().Be(testObject.String_Requires); 
    } 

    public void Set_Value_Of_Property() 
     { 
      var testObject = new ReflectedType 
      { 
       AReferenceType_No_Attributes = new object(), 
       Int32WithRange1_10 = 5, 
       String_Requires = "Test String" 
      }; 

      testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC"); 

      testObject.String_Requires.Should().Be("MAGIC"); 
     } 

당신 람다는 PropertyInfo 오브젝트를 기반에서는 dynamicCast를 호출하는 것을 피하기 위해 입력 된 전화를 할 수 있도록하기 위해 MakeGenericMethod 또는 식 트리를 사용하는 도우미 메서드를 작성할 수 그것을 알기 위해. 하지만 덜 우아합니다.