ラムダ式をコンパイルすると、Closure引数を持つ代理人になります

c# expression-trees lambda

質問

式ツリーからデリゲートを作成するためにExpression.Lambda( ... ).Compile()を使用すると、結果は最初の引数がClosureであるデリゲートになります。

public static Func<T, T, T> CreateTest<T>()
{
    ParameterExpression a = Expression.Parameter( typeof( T ) );
    ParameterExpression b = Expression.Parameter( typeof( T ) );
    Expression addition = Expression.Add( a, b );

    return (Func<T, T, T>)Expression.Lambda( addition, a, b ).Compile();
}

...

// 'addition' equals
// Int32 lambda_method(
//     System.Runtime.CompilerServices.Closure,
//     Int32,
//     Int32 )
Func<int, int, int> addition = DelegateHelper.CreateTest<int>();
int result = addition( 5, 5 );

Closureオブジェクトを渡すことなく、通常のコードでデリゲートを簡単に呼び出すことはできますが、このClosureどこから来たのですか?

どのように私はこの代理人を動的に呼び出すことができますか?

// The following does not work.
// Exception: MethodInfo must be a runtime MethodInfo object.    
MethodInfo additionMethod = addition.Method;
int result = (int)additionMethod.Invoke( null, new object[] { 5, 5 } );

式ツリーを使用すると、 Closureオブジェクトを渡す必要があるように見えます。

PropertyInfo methodProperty
    = typeof( Delegate ).GetProperty( "Method", typeof( MethodInfo ) );
MemberExpression getDelegateMethod
    = Expression.Property( Expression.Constant( addition ), methodProperty );
Func<MethodInfo> getMethodInfo
    = (Func<MethodInfo>)Expression.Lambda( getDelegateMethod ).Compile();
// Incorrect number of arguments supplied for call to method
// 'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32, Int32)'
Expression call
    = Expression.Call(
        getMethodInfo(),
        Expression.Constant( 5 ), Expression.Constant( 5 ) );

これは、それ自体が意味をなさない単純化された例です。私が実際に達成しようとしているのは、 Func<Action<SomeObject>>Func<Action<object>>とラップすることです。私はすでにネストされていない代理人のためにこれを行うことができます。これは、 ここで説明するように 、リフレクション中に便利です。

このClosureオブジェクトを正しく初期化するにはどうすればよいですか?

受け入れられた回答

表示されるClosureタイプは実装の詳細です。 MSDNはそれについてかなり明白です:

このAPIは.NET Frameworkインフラストラクチャをサポートしており、コードから直接使用するためのものではありません。動的に生成されるメソッドの実行時の状態を表します。

式ツリーは状態を持つことができます。

Closureインスタンスには、ラムダ式がうまく閉じているすべての非リテラル定数が格納されます。また、式ツリーにネストされたラムダのデリゲートの連鎖を含めることもできます。

これを実現するために、式ツリーコンパイラはかわいいトリックを使用します。これはDynamicMethodを使用してメモリコードを生成します。これは定義上は静的です。それでも、彼らは最初の議論を終えたデリゲートを作成しています。これは、CLRがデリゲートのターゲットフィールドを静的メソッドの最初の引数として渡すことを意味します。あなたからClosureの引数を効果的に隠しています。

あなたの問題の解決法は簡単です。メソッドを呼び出そうではなく、リフレクションを使用している場合はDelegate.DynamicInvokeを使用するか、式ツリーのコンテキストでExpression.Invokeを使用してデリゲートを呼び出してください。



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ