Создайте выражение для вызова метода без параметра

Я пытаюсь создать выражение, которое вызывает внутренний метод, внутренний метод имеет параметр out, возможно ли это?

public class Program
{
    static void Main(string[] args)
    {
        var type = typeof (Program);
        var methodInfo = type.GetMethod("ValidateActiveControl", BindingFlags.Instance | BindingFlags.NonPublic);

        var p1 = Expression.Parameter(type, "program");
        var p2 = Expression.Parameter(typeof (bool), "validatedControlAllowsFocusChange");

        var invokeExpression = Expression.Call(p1, methodInfo, p2);
        var func = (Func<Program,bool, bool>)Expression.Lambda(invokeExpression, p1, p2).Compile();

        var validatedControlAllowsFocusChange = true;
        // I would expect validatedControlAllowsFocusChange to be false after execution...
        Console.WriteLine(func.Invoke(new Program(), validatedControlAllowsFocusChange));
        Console.WriteLine(validatedControlAllowsFocusChange);

    }

    internal bool ValidateActiveControl(out bool validatedControlAllowsFocusChange)
    {
        validatedControlAllowsFocusChange = false;

        // Some code here...

        return true;
    }
}

person Rohan West    schedule 30.06.2010    source источник


Ответы (2)


В дополнение к замечанию Джима о MakeByRefType вам нужно будет создать собственный тип делегата, поскольку Func не поддерживает параметры ref или out. Собираем все вместе:

delegate bool ValidateDelegate(Program program, out bool validatedControlAllowsFocusChange);

static void Main(string[] args)
{
    var type = typeof(Program);
    var methodInfo = type.GetMethod("ValidateActiveControl", BindingFlags.Instance | BindingFlags.NonPublic);

    var p1 = Expression.Parameter(type, "program");
    var p2 = Expression.Parameter(typeof(bool).MakeByRefType(), "validatedControlAllowsFocusChange");

    var invokeExpression = Expression.Call(p1, methodInfo, p2);
    var func = Expression.Lambda<ValidateDelegate>(invokeExpression, p1, p2).Compile();

    var validatedControlAllowsFocusChange = true;
    // I would expect validatedControlAllowsFocusChange to be false after execution...
    Console.WriteLine(func.Invoke(new Program(), out validatedControlAllowsFocusChange));
    Console.WriteLine(validatedControlAllowsFocusChange);

}

Изменить: это работает в .NET 4.0, но не в .NET 3.5. Платформа 3.5 не поддерживает деревья лямбда-выражений с параметрами out или ref. Этот код:

delegate void RefTest(out bool test);

static void Main(string[] args)
{
    var p1 = Expression.Parameter(typeof(bool).MakeByRefType(), "test");
    var func = Expression.Lambda<RefTest>(Expression.Constant(null), p1);
}

выдает исключение ArgumentException «Лямбда-выражение не может содержать параметры передачи по ссылке». Я не думаю, что вы можете делать то, что хотите, без обновления до .NET 4.0.

person Quartermeister    schedule 30.06.2010
comment
Это по-прежнему вызывает ошибку. Выражение типа «System.Boolean» не может использоваться для параметра типа «System.Boolean» метода «Boolean ValidateActiveControl (Boolean ByRef)». - person Rohan West; 30.06.2010
comment
Хорошая находка в .NET 4. Непередача лямбда-выражений по ссылке в 3.5 является серьезным ограничением. - person Jim Schubert; 30.06.2010

Согласно этой записи в блоге, вам необходимо сделать следующее:

typeof(bool).MakeByRefType();

person Jim Schubert    schedule 30.06.2010
comment
Это работает с использованием отражения, когда я создаю выражение, я получаю следующую ошибку. Выражение типа «System.Boolean» не может использоваться для параметра типа «System.Boolean» метода «Boolean ValidateActiveControl (Boolean ByRef)». - person Rohan West; 30.06.2010
comment
Извините, что не сработало. Эта ошибка кажется действительно странным (и ненужным) требованием для лямбда-выражений. - person Jim Schubert; 30.06.2010