2013-05-08 8 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を発見したグーグルの時間後。それは、同じ悩みを持つ人を助けるかもしれないように私はブログの記事からの抜粋を追加しました:

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

私が直接値を設定して使用することが少し楽に実際に上記gsharpのポストを修正しました。 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または式ツリーを使用するヘルパーメソッドを書くことができそれを前もって知っている。しかしそれはあまりエレガントではありません。

関連する問題