2012-10-27 9 views
6


を使用してオブジェクトを作成しはじめ動的反射、チェーン方法やラムダ式

私のアプリケーションは、メソッドチェーンを使用してオブジェクトをインスタンス化します。

var car = new Car("Ferrari").Doors(2).OtherProperties(x = x.Color("Red")); 


問題

私には要件があります実行時にこのオブジェクトを動的に生成する - 構成に必要な連鎖されたメソッドは実行時に決定されるため、すべてを動的に組み立てる必要があります。私は過去に反射を使ってnew Car("Ferrari", 2, "Red")のような単純なオブジェクトを作成しました - それで涼しいですが、ラムダ式をパラメータとして持つ連鎖メソッドのものは絶対にありません。私は表現木を見て、これが動的表現パラメータを作成するソリューションの一部だと考えていますが、基本オブジェクトと追加の連鎖メソッドを作成するためにリフレクションと一緒にステッチする方法を理解しようとしています。


おかげで、私の問題で、あなたが提供することができるかもしれない任意の指導や情報を探すために時間を割いて、事前に感謝


UPDATE:結論その答えのためのdasblinkenlightとジョンスキートに

感謝を。彼のコードサンプルがすぐに私を離れてすぐ走ったので、私はdasblinkenlightの答えを選びました。メソッド連鎖については、私は基本的に受け入れられた答えで同じループの手法を使用したので、私はそのコードを繰り返しませんが、以下では、表現ツリーメソッドの呼び出しをリフレクションによって実行できるアクションデリゲートに動的に変換するコードを示します。Invoke() dasblinkenlightの答えで。これは、ジョンが指摘したように、本当に問題の要点でした。

メソッドメタデータを格納するヘルパークラス。

public struct Argument 
    { 
     public string TypeName; 
     public object Value; 
    } 

public class ExpressionTreeMethodCall 
{ 
    public string MethodName { get; set; } 
    public IList<Argument> Arguments { get; set; } 

    public ExpressionTreeMethodCall() 
    { 
     Arguments = new List<Argument>(); 
    } 
} 


staticメソッドラムダ式のメソッド呼び出しを組み立てた後、(私の場合はInvoke()への引数として渡された)他の場所で実行するアクションデリゲートとしてそれを返します。

public static Action<T> ConvertExpressionTreeMethodToDelegate<T>(ExpressionTreeMethodCall methodData) 
    {    
     ParameterExpression type = Expression.Parameter(typeof(T)); 

     var arguments = new List<ConstantExpression>(); 
     var argumentTypes = new List<Type>(); 

     foreach (var a in methodData.Arguments) 
     { 
      arguments.Add(Expression.Constant(a.Value)); 
      argumentTypes.Add(Type.GetType(a.TypeName)); 
     } 

     // Creating an expression for the method call and specifying its parameter. 
     MethodCallExpression methodCall = Expression.Call(type, typeof(T).GetMethod(methodData.MethodName, argumentTypes.ToArray()), arguments); 

     return Expression.Lambda<Action<T>>(methodCall, new[] { type }).Compile(); 
    } 

答えて

2

次の2つの別々の問題に直面しています。

あなたは次の情報が使用可能であることを言ってみましょう:

  • チェーン(コンストラクタ)
  • MethodInfoの配列をコンストラクタのパラメータを表すオブジェクトの配列の最初の項目を表すConstructorInfoオブジェクト - 各連鎖関数の1つ
  • 各連鎖関数のパラメータを表すオブジェクト配列の配列

そして、その結果を構築するプロセスは、次のようになります。

ConstructorInfo constr = ... 
object[] constrArgs = ... 
MethodInfo[] chainedMethods = ... 
object[][] chainedArgs = ... 
object res = constr.Invoke(constrArgs); 
for (int i = 0 ; i != chainedMethods.Length ; i++) { 
    // The chaining magic happens here: 
    res = chainedMethods[i].Invoke(res, chainedArgs[i]); 
} 

ループが終了すると、あなたのresが設定されたオブジェクトが含まれています。

上記のコードでは、チェーンメソッドの中に汎用メソッドがないと仮定しています。メソッドの一部がジェネリックである場合は、Invokeを呼び出す前に、ジェネリックメソッドの呼び出し可能インスタンスを作成するための追加手順が必要です。

ここで、ラムダを見てみましょう。メソッドに渡されるラムダのタイプに応じて、特定のシグネチャを持つデリゲートを渡す必要があります。メソッドを呼び出し可能な代理人に変換するには、System.Delegateクラスを使用できる必要があります。必要な代理人を実装するサポートメソッドを作成する必要があるかもしれません。あなたがリフレクションを通して呼び出すことができなければならない正確な方法を見ることなく、どうやって言うことが難しいです。表現木をコンパイルしてから、Func<...>インスタンスを取得する必要があります。 - 「makeRed」Actionオブジェクトが「chainedArgs」に保存されているとの間に「呼び出し()」の呼び出し中に実行することができ

Expression arg = Expression.Parameter(typeof(MyCarType)); 
Expression red = Expression.Constant("Red"); 
MethodInfo color = typeof(MyCarType).GetMethod("Color"); 
Expression call = Expression.Call(arg, color, new[] {red}); 
var lambda = Expression.Lambda(call, new[] {arg}); 
Action<MyCarType> makeRed = (Action<MyCarType>)lambda.Compile(); 
+0

感謝ダスは...そう、あなたの提供されたコードが使用して:x.Color("Red")の呼び出しは次のようになりますチェーンループ? – mmacneil007

+0

@ mmacneil007絶対に、それは考えです。 'Action 'は、配列の 'chainedArgs'配列内の配列の1つに格納され、対応するメソッド(あなたの場合は' OtherProperties')に渡すことができるデリゲートです。 – dasblinkenlight

+0

優秀、私はこれが正しい道に私を置いたと信じています。まだ表現の木の周りに私の頭を包んでいるが、私はあなたがここで概説したアプローチはトリックを行うべきだと思う。再度、感謝します! – mmacneil007

1

決してまあ

をパラメータとしてラムダ式を含む連鎖方法で何か、チェーンの方法が少しあります。それは数回の反射を使用するだけの問題です。チェーン一緒

foo.X().Y() 

に以下を行う必要があります。

  • は、呼び出し対象としてfooの値を用いた反射を経由してメソッドを呼び出し、そして覚えfoo
  • の宣言された型からメソッドXを取得します結果(例:tmp
  • の宣言された型からYメソッドを取得するXMethodInfo.ReturnTypeを参照)
  • ラムダ式が困難である

呼び出し対象として前の結果(tmp)を用いた反射を経由してメソッドを呼び出します - それは本当にあなたが式を提供することになるだろうどのように依存します最初は合理的に任意の式を実行するデリゲートを構築することは、expression treesを使用してもそれほど難しくないし、LambdaExpression.Compileを呼び出すことですが、何をしているのかを知る必要があります。別に2と

レッツ・契約をパラメータとしてラムダを取るメソッドの呼び出し

  • 呼び出しチェーン方法、および