creating and calling delegates

Jan 5, 2010 at 5:47 PM

If in the following class SubroutineCaller is called "47" and "0" are printed. I would expect "47" and "47" to be printed instead. I imagine there is some type of scope issue that I am not fully aware of.

How can I get the ParameterExpressions that are passed to the BlockExpression "block" be the same that are in the parent? I have written this code two ways. The first way the delegate is compiled and invoked at runtime. In the second way the delegate is compiled at compile time. Both perform the same way: "47" and "0" are printed.

Any help would be greatly appreciated. Suggestions on alternative ways to achieve the same goal would also be much appreciated.

thanks

public class CompileAtRuntime
{
    public static void PrintLong(long a)
    {
        Trace.WriteLine(a);
    }

    public static void RunBlock(BlockExpression block)
    {
        Expression.Lambda(block).Compile().DynamicInvoke();
    }

    public static void SubroutineCaller()
    {
        ParameterExpression p = Expression.Parameter(typeof(long), "a");
        
        BlockExpression block = BlockExpression.Block(
            new ParameterExpression[] { p },
            new Expression[]
            {
                Expression.Call(
                    typeof(CompileAtRuntime).GetMethod("PrintLong"),
                    p
                )
            }
        );

        BlockExpression parent = BlockExpression.Block(
            new ParameterExpression[] { p },
            new Expression[]
            {
                Expression.Assign(p, Expression.Constant(47L, typeof(long))),
                Expression.Call(
                    typeof(CompileAtRuntime).GetMethod("PrintLong"),
                    p
                ),
                Expression.Call(
                    typeof(CompileAtRuntime).GetMethod("RunBlock"),
                    Expression.Constant(block)
                )
            }
        );

        Expression.Lambda(parent).Compile().DynamicInvoke();
    }
}

public class CompileEverything
{
    public static void PrintLong(long a)
    {
        Trace.WriteLine(a);
    }

    public static void RunBlock(Delegate del)
    {
        del.DynamicInvoke();
    }

    public static void SubroutineCaller()
    {
        ParameterExpression p = Expression.Parameter(typeof(long), "a");

        BlockExpression block = BlockExpression.Block(
            new ParameterExpression[] { p },
            new Expression[]
            {
                Expression.Call(
                    typeof(CompileEverything).GetMethod("PrintLong"),
                    p
                )
            }
        );

        Delegate del = Expression.Lambda(block).Compile();


        BlockExpression parent = BlockExpression.Block(
            new ParameterExpression[] { p },
            new Expression[]
            {
                Expression.Assign(p, Expression.Constant(47L, typeof(long))),
                Expression.Call(
                    typeof(CompileEverything).GetMethod("PrintLong"),
                    p
                ),
                Expression.Call(
                    typeof(CompileEverything).GetMethod("RunBlock"),
                    Expression.Constant(del)
                )
            }
        );

        Expression.Lambda(parent).Compile().DynamicInvoke();
    }
}

Jan 6, 2010 at 10:56 AM
Edited Jan 6, 2010 at 10:57 AM

Despite the fact that I'm new to the DLR, I will do my best to try to help you. I've modified the code from the CompileEverything class to do what you want. If I understood it correct you want to achieve a closure effect that results in var block and var parent referring to the same variable p. What you're missing (I think) is that you're calling .Compile() two times on the two different lambdas, when you really should be calling compile one time (on the parent).

If you think about it for a minute it makes sense, in essence what you're doing now is two different compilation units, the first (block) being called by the second (parent). They have no sense of relation to one another, block isn't nested inside parent - you can think of it as existing next to parent, and parent doesn't know about block either. What you want to do is compile block inside the context of parent.

    using Et = System.Linq.Expressions.Expression;
    using System.Diagnostics;

    public class CompileEverything
    {
        public static void PrintLong(long a)
        {
            Console.WriteLine(a);
        }

        public static void SubroutineCaller()
        {
            var p = Et.Parameter(typeof(long), "a");

            Et block = Et.Lambda(
                Et.Call(
                    typeof(CompileEverything).GetMethod("PrintLong"),
                    p
                )
            );

            var parent = Et.Block(
                new [] { p },
                Et.Assign(p, Et.Constant(47L, typeof(long))),
                Et.Call(
                    typeof(CompileEverything).GetMethod("PrintLong"),
                    p
                ),
                Et.Invoke(
                    block
                )
            );

            Et.Lambda(parent).Compile().DynamicInvoke();
        }
    }

In your previous example you were calling block from RunBlock which had no sense of it's owning scope (it's just a call out from the DLR to the CLR), you need to compile and call your subroutine inside the owning block/routines scope. That's exactly how closures work, they have to be nested inside their parent scope. You can't define a closure next to another function and then tell the compiler/runtime that "hey, this one should be able to execute inside this other ones context".

I hope I explained it for you, and that I was correct (as I said, I am new to the DLR). The code above produces what you expected, that is 42 42 instead of 42 0

Jan 6, 2010 at 5:52 PM

That was the exact fix I was looking for. That helped a lot. I was overlooking Invoke.

thanks a ton