2012-01-27 6 views
4

私がやりたいことは、どのクラスタイプでも、オブジェクトグラフのすべてのプロパティに「取得」アクセサのリストを作成することです。プログラムでプロパティアクセサファンクションのコレクションを作成する

コレクションの形式、順序などは問題ではありません。すべてのプロパティのアクセサーを識別して作成する方法はわかりません。

public static List<Func<T,object>> CreateAccessors<T>() 
{ 
    Type t = typeof(T); 
    // Identify all properties and properties of properties (etc.) of T 
    // Return list of lambda functions to access each one given an instance of T 
} 

public void MyTest() 
{ 
    MyClass object1; 
    var accessors = CreateAccessors<MyClass>(); 
    var myVal1 = accessors[0](object1); 
    var myVal2 = accessors[1](object1); 

    // myVal1 might now contain the value of object1.Property1 
    // myVal2 might now contain the value of object1.Property4.ThirdValue.Alpha 
} 
+0

Jon Skeetのこのブログ記事 - https://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspxを見てください多くの場合、再帰を禁止します。 –

+0

あなたもこれを見たいと思うかもしれません。http://stackoverflow.com/questions/16578857/using-nested-collections-of-lambda-expressions-to-create-an-object-graph – nawfal

+0

JonのMVPブログがなくなりました。彼の個人的なブログの投稿のコピー: https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/ –

答えて

5

リフレクションを使用してプロパティと式ツリーを抽出して、代理人targeti

   // Start with all public instance properties of type 
var accessors = from property in type.GetProperties 
         (BindingFlags.Instance | BindingFlags.Public) 

       // Property must be readable 
       where property.CanRead 

       //Assemble expression tree of the form: 
       // foo => (object) foo.Property 

       // foo 
       let parameter = Expression.Parameter(type, "foo") 

       // foo.Property 
       let propertyEx = Expression.Property(parameter, property) 

       // (object)foo.Property - We need this conversion 
       // because the property may be a value-type. 
       let body = Expression.Convert(propertyEx, typeof(object)) 

       // foo => (object) foo.Property 
       let expr = Expression.Lambda<Func<T,object>>(body, parameter) 

       // Compile tree to Func<T,object> delegate 
       select expr.Compile(); 

return accessors.ToList(); 

Delegate.CreateDelegateは当然の選択のように思えるが、あなたが値型のプロパティをボクシングいくつかの問題を持っていること:プロパティのゲッターをngの。式の木はこの問題を優雅にかわす。

「入れ子になった」プロパティも取り出せるようにするためにはもっと多くの作業が必要ですが、うまくいけば、ypuを起動しておくことができます(ヒント:recurse)。最終的なポインタ1つ:オブジェクトグラフのサイクルを監視してください!

+0

もう一つの考慮点は、ネストされたオブジェクトではnullです。 'b'がnullの場合、' a.b.c'が返すものは何でしょうか? – Gabe

+0

応答のおかげで@Ani。しかし、私にとって最も面倒なことを証明するのは再帰部分です。私は表現木が新しく、より深い特性にアクセスする式を構築するような再帰的な方法をあまり理解していません。より多くの情報への例やリンクがありますか? さらに、状況にもう1つをスローするには、プロパティのいくつかをインデックス配列にすることができます。私はこれにどのように対処しますか? – cachance7

+0

'let'?これはC#ですか? –

0
public static List<Func<T, object>> CreateAccessors<T>() 
{ 
    var accessors = new List<Func<T, object>>(); 
    Type t = typeof(T); 
    foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { 
     if (prop.CanRead) { 
      var p = prop; 
      accessors.Add(x => p.GetValue(x, null)); 
     } 
    } 
    return accessors; 
} 

EDIT:ここ

がコンパイル表現を返し変種であると反射

を使用して、以前のものよりもはるかに高速である必要があり、それはこのような何かの形をとるかもしれません
public static List<Func<T, object>> CreateAccessorsCompiled<T>() 
{ 
    var accessors = new List<Func<T, object>>(); 
    Type t = typeof(T); 
    foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { 
     if (prop.CanRead) { 
      ParameterExpression lambdaParam = Expression.Parameter(t, "instance"); 
      Expression bodyExpression; 
      MemberExpression memberAccessExpression = Expression.MakeMemberAccess(Expression.Convert(lambdaParam, t), prop); 
      if (prop.PropertyType == typeof(object)) { // Create lambda expression: (instance) => ((T)instance).Member 
       bodyExpression = memberAccessExpression; 
      } else { // Create lambda expression: (instance) => (object)((T)instance).Member 
       bodyExpression = Expression.Convert(memberAccessExpression, typeof(object)); 
      } 
      var lambda = Expression.Lambda<Func<T, object>>(bodyExpression, lambdaParam); 
      accessors.Add(lambda.Compile()); 
     } 
    } 
    return accessors; 
} 
関連する問題