9

If I wanted to create an expression tree that called a method with an out parameter and then returned the out value as a result.. how would I go about it?

The following does not work (throws a runtime exception), but perhaps best demonstrates what I'm trying to do:

private delegate void MyDelegate(out int value);
private static Func<int> Wrap(MyDelegate dele)
{
    MethodInfo fn = dele.Method;
    ParameterExpression result = ParameterExpression.Variable(typeof(int));
    BlockExpression block = BlockExpression.Block(
        typeof(int), // block result
        Expression.Call(fn, result), // hopefully result is coerced to a reference
        result); // return the variable
    return Expression.Lambda<Func<int>>(block).Compile();
}

private static void TestFunction(out int value)
{
    value = 1;
}

private static void Test()
{
    Debug.Assert(Wrap(TestFunction)() == 1);
}

I know this can be fairly easily solved in raw IL (or indeed without runtime compilation at all), but unfortunately this is part of a much larger expression building process... so I'm really hoping this isn't a limitation, as a complete rewrite would be more than a bit of a pain.

Mania
  • 1,718
  • 14
  • 16
  • 1
    Lambda functions certainly can call methods that have `ref`/`out` parameters (as in question), what they cannot do is refer to the enclosing method's `ref`/`out` parameters. – Mania Dec 14 '11 at 13:44

2 Answers2

9

This works for me:

    private static Func<int> Wrap(MyDelegate dele)
    {
        var fn = dele.Method;
        var result = ParameterExpression.Variable(typeof(int));
        var block = BlockExpression.Block(
            typeof(int),
            new[] { result },
            new Expression[]
            {
                Expression.Call(fn, result),
                result,
            });
        return Expression.Lambda<Func<int>>(block).Compile();
    }
max
  • 33,369
  • 7
  • 73
  • 84
-4

Maybe it's just me, but I don't really see the point of the whole thing. To accomplish what you're trying to do, you don't really need to write all that stuff.

Sample code in a console app:

    class Program
    {
        static void Main(string[] args)
        {
            var temp = Execute(DoSomething);
            Console.Write(temp);
            Console.Read();
        }

        static int Execute(Func<int> methodToRun)
        {
            return methodToRun.Invoke();
        }

        static int DoSomething()
        {
            return 1;
        }
    }

As you see it gets you the same results in a more concise and clean way. What I think you were missing out is that Action, Action<> and Func<> are all sintactic sugar for delegate so no need to mix the 2 syntaxes and no need to reconstruct the whole expression like you are doing.

Matteo Mosca
  • 7,380
  • 4
  • 44
  • 80
  • The point of the exercise is runtime code generation. ie, what if you were passed a completely arbitrary `MethodInfo`, and wanted to be able to twiddle the results/parameters a bit with the minimal possible performance penalty. You could not call the method directly, as you don't know the detail of it at compile time, you could use reflection to invoke the method at runtime, but that'd be very slow.. Expression.Compile allows you to do a bit more work the first time, but create a method that allows you to do it repeatedly at minimal cost for future invocations. This is the point ;) – Mania Dec 14 '11 at 13:38
  • Well now that you explain it, but from the code you wrote it didn't appear like that. Still that delegate MyDelegate(out int value) is equivalent to Func MyDelegate. Func is also the return type of Wrap. That, to me, seems pointless: the method gets a Func as parameter and outputs the same Func as result... no, still not convincing :) – Matteo Mosca Dec 14 '11 at 13:44
  • Read the bit immediately following the sample code ;), I acknowledged that runtime code generation is not required here, but explained that it's part of a larger problem. The sample code only served to demonstrate a minimal, simple version of the problem at hand - to include the full code would require several large .cs files. I did consider making the test case `int.TryParse` (which returns a `bool` and an `out int`), and requiring the Expression to combine them both into a `Tuple`.. but that seemed needlessly complicated. (And could also be solved without runtime code generation) – Mania Dec 14 '11 at 13:49
  • I can see your point, I did a lot of expression tree parsing and runtime code generation in the past 2 years. :) – Matteo Mosca Dec 14 '11 at 13:51