2013-05-08 55 views
8

Tôi muốn tạo biểu thức Lambda cho mọi thuộc tính của đối tượng đọc giá trị động.Đọc thuộc tính của đối tượng với cây biểu thức

Những gì tôi có cho đến nay:

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(); 
} 

Mã Hoạt động tốt khi tôi gọi functionThatGetsValue chừng nào "TypeOfProperty" là mã hóa cứng. Tôi biết rằng tôi không thể vượt qua "TypeOfPoperty" động. Tôi có thể làm gì để đạt được mục tiêu của mình?

+0

Mục tiêu của bạn là gì? Bạn nói rằng bạn muốn tạo ra một biểu thức lambda; bạn chỉ cần ủy nhiệm được biên dịch ('functionThatGetsValue'), hay bạn cũng cần cây biểu thức trung gian (' expression')? – LukeH

+0

@ LukeH, chỉ là đại biểu được biên soạn. cảm ơn. (Mục tiêu của tôi là lặp qua danh sách các đối tượng và đọc tất cả các giá trị từ các thuộc tính. Để đạt được một chút hiệu suất tôi muốn làm theo cách này thay vì sử dụng sự phản chiếu) – gsharp

+1

Khi tôi cố gắng đạt được kết quả tương tự, tôi đã kết thúc bằng trả về Func và truyền giá trị trả lại cho loại thuộc tính cụ thể trên bên của người gọi. –

Trả lời

7

Giả sử rằng bạn hài lòng với một đại biểu Func<TType, object> (theo ý kiến ​​ở trên), bạn có thể sử dụng Expression.Convert để đạt được điều đó:

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

Sau giờ googling tìm thấy câu trả lời here. Tôi đã thêm các đoạn từ bài viết trên blog vì nó có thể giúp đỡ người khác có những rắc rối tương tự:

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

Tôi đã sửa đổi bài gsharp của trên để thực sự thiết lập giá trị trực tiếp và làm cho nó dễ dàng hơn một chút để sử dụng. Nó không lý tưởng vì có sự ra đời của chức năng DynamicCast mà đòi hỏi bạn phải biết loại của bạn lên phía trước. Mục tiêu của tôi là cố gắng giữ cho chúng tôi gõ mạnh mẽ và không trả lại đối tượng và tránh từ khóa động. Ngoài ra, hãy giữ "ma thuật" ở mức tối thiểu.

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); 
    } 

Ví dụ:

 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"); 
     } 

Bạn thể viết một phương thức helper trong đó sử dụng MakeGenericMethod hoặc một cây biểu thức để thực hiện một lambda làm các cuộc gọi đánh máy để gọi DynamicCast dựa trên đối tượng PropertyInfo và tránh việc để biết nó lên phía trước. Nhưng đó là ít thanh lịch.

Các vấn đề liên quan