Компиляция выражения лямбда приводит к тому, что делегат имеет аргумент 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
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему