Create expression to invoke method with out parameter
Asked Answered
R

2

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;
    }
}
Rhonda answered 30/6, 2010 at 3:41 Comment(0)
I
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.

Idolum answered 30/6, 2010 at 3:58 Comment(2)
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)'Rhonda
Nice find on the .NET 4. Lambdas not passing by reference in 3.5 is a serious limitation.Mcandrew
M
1

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

typeof(bool).MakeByRefType();

Mcandrew answered 30/6, 2010 at 3:46 Comment(2)
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)'Rhonda
Sorry that didn't work. This error seems to be a really weird (and unnecessary) requirement for lambdas.Mcandrew

© 2022 - 2024 — McMap. All rights reserved.