12

I am trying to create an expression that invokes an internal method, the internal method has an out parameter, is this possible?

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;
    }
}

2 Answers 2

15

In addition to Jim's point about MakeByRefType, you will need to create a custom delegate type, since Func does not support ref or out parameters. Putting it all together:

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);

}

Edit: This works in .NET 4.0, but not in .NET 3.5. The 3.5 Framework doesn't appear to support lambda expression trees with out or ref parameters. This code:

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);
}

throws an ArgumentException "A lambda expression cannot contain pass by reference parameters". I don't think you can do what you want without upgrading to .NET 4.0.

Sign up to request clarification or add additional context in comments.

2 Comments

This still causes an error, Expression of type 'System.Boolean&' cannot be used for parameter of type 'System.Boolean' of method 'Boolean ValidateActiveControl(Boolean ByRef)'
Nice find on the .NET 4. Lambdas not passing by reference in 3.5 is a serious limitation.
1

According to this blog post, you'd need to do:

typeof(bool).MakeByRefType();

2 Comments

This works using reflection, when i create an expression i get the following error Expression of type 'System.Boolean&' cannot be used for parameter of type 'System.Boolean' of method 'Boolean ValidateActiveControl(Boolean ByRef)'
Sorry that didn't work. This error seems to be a really weird (and unnecessary) requirement for lambdas.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.