2011-12-14 9 views
7

outパラメータを持つメソッドを呼び出した式ツリーを作成し、その結果としてout値を返した場合、どうすればよいでしょうか?ByRefパラメータでC#の式ツリーを使用

次は(実行時例外がスローされます)は動作しませんが、おそらく最高のは、私が何をしようとしている示しています

private delegate void MyDelegate(out int value); 
private static Func<int> Wrap(MyDelegate dele) 
{ 
    MethodInfo fn = dele.Method; 
    ParameterExpression result = ParameterExpression.Variable(typeof(int)); 
    BlockExpression block = BlockExpression.Block(
     typeof(int), // block result 
     Expression.Call(fn, result), // hopefully result is coerced to a reference 
     result); // return the variable 
    return Expression.Lambda<Func<int>>(block).Compile(); 
} 

private static void TestFunction(out int value) 
{ 
    value = 1; 
} 

private static void Test() 
{ 
    Debug.Assert(Wrap(TestFunction)() == 1); 
} 

私はこれはかなり簡単に生のILで解決(または実際にせずにすることができます知っていますランタイムコンパイルはまったくありません)、残念ながら、これははるかに大きなエクスプレッション構築プロセスの一部です...私は本当にこれが限界ではないことを願っています。完全な書き換えは少しの痛みではないでしょう。

+1

ラムダ関数は、(問題のように) 'ref' /' out'パラメータを持つメソッドを呼び出すことができますが、囲むメソッドの 'ref' /' out'パラメータを参照することはできません。 – Mania

答えて

7

これが私の作品:

private static Func<int> Wrap(MyDelegate dele) 
    { 
     var fn = dele.Method; 
     var result = ParameterExpression.Variable(typeof(int)); 
     var block = BlockExpression.Block(
      typeof(int), 
      new[] { result }, 
      new Expression[] 
      { 
       Expression.Call(fn, result), 
       result, 
      }); 
     return Expression.Lambda<Func<int>>(block).Compile(); 
    } 
-4

は、多分それは私だけだが、私は本当に全部のポイントが表示されていません。あなたがしようとしていることを達成するために、あなたは本当にすべてのものを書く必要はありません。コンソールアプリで

サンプルコード:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var temp = Execute(DoSomething); 
      Console.Write(temp); 
      Console.Read(); 
     } 

     static int Execute(Func<int> methodToRun) 
     { 
      return methodToRun.Invoke(); 
     } 

     static int DoSomething() 
     { 
      return 1; 
     } 
    } 

あなたはそれはあなたより簡潔かつクリーンな方法で同じ結果を得る見ての通り。私があなたが欠けていたと思うのはdelegateのためにすべてActionAction<>およびFunc<>はすべての砂糖であるので、2つの構文を混ぜる必要はなく、あなたがしているように全体の表現を再構築する必要はありません。

+0

練習のポイントは、ランタイムコードの生成です。つまり、完全に恣意的な 'MethodInfo'を渡した場合、結果/パラメータを最小限のパフォーマンス上のペナルティでちょっとひっくり返すことができるようにしたければどうでしょうか?コンパイル時にその詳細を知らないので、メソッドを直接呼び出すことはできませんでした。実行時にリフレクションを使用してメソッドを呼び出すことができましたが、それは非常に遅いでしょう。Expression.Compileでは、初めての作業ですが、将来の呼び出しで最小限のコストで繰り返し実行できるメソッドを作成してください。これはポイントです;) – Mania

+0

さて、あなたはそれを説明しましたが、あなたが書いたコードから、それはそのようには見えませんでした。それでも、MyDelegate(int値)の代理人はFunc MyDelegateに相当します。 Func もWrapの戻り値の型です。それは私には意味がないようです:メソッドはFunc をパラメータとして得て、同じFunc を結果として出力します。いいえ、まだ説得力がありません:) –

+0

サンプルコードの直後のビットを読む;)、コード生成はここでは必要ではありませんが、より大きな問題の一部であると説明されています。サンプルコードは、手元の問題の最小限でシンプルなバージョンを示すためにのみ役立ちました。フルコードを含めるには、いくつかの大きな.csファイルが必要でした。私はテストケース 'int.TryParse'(' bool'と 'int'を返す)を作成し、それらの両方を' Tuple'に組み合わせることを要求しました。しかし、それは不必要に複雑に思えました。 (また、ランタイムコード生成なしで解決することができます) – Mania

関連する問題