Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Supporting "out / ref" parameters in expressions with conversion to "object"

My journey to this question started with an implementation of Jon Skeet's article: "Making reflection fly and exploring delegates":

and in it, he states:

Note: I was going to demonstrate this by calling DateTime.AddDays, but for value type instance methods the implicit first first parameter is passed by reference, so we’d need a delegate type with a signature of DateTime Foo(ref DateTime original, double days) to call CreateDelegate. It’s feasible, but a bit of a faff. In particular, you can’t use Func<...> as that doesn’t have any by-reference parameters.

I am using my own delegates in an effort to support this and I am just totally stumped trying to create compiled expressions in C# .NET 4.0 to support "out / ref" but converting the delegate types to "object".

When I run the code below, I get the expected output in the first case, however, in the second case (when I convert the input and output parameters to type: object) the "out" parameter is not assigned and the result is "before" as opposed to "after".

public class Test
    {
        public delegate void myDelegate<T, U>(T test, out U setMe);

        public void myFunction(out string setMe)
        {
            setMe = "after";
        }
    }

private static playin.program.Test.myDelegate<Test, string> buildExactExpression(MethodInfo methodInfo)
    {
        ParameterExpression instance = Expression.Parameter(typeof(Test));
        ParameterExpression argument = Expression.Parameter(typeof(string).MakeByRefType());

        var methodCall = Expression.Call(
                         instance,
                         methodInfo,
                         argument);

        return Expression.Lambda<playin.program.Test.myDelegate<Test, string>>(methodCall, new ParameterExpression[] { instance, argument }).Compile();
    }

    private static playin.program.Test.myDelegate<object, object> buildDesiredExpression(MethodInfo methodInfo)
    {
        ParameterExpression instance = Expression.Parameter(typeof(object));
        ParameterExpression argument = Expression.Parameter(typeof(object).MakeByRefType());

        var methodCall = Expression.Call(
                         Expression.Convert(instance, typeof(Test)),
                         methodInfo,
                         Expression.Convert(argument, typeof(string)));

        return Expression.Lambda<playin.program.Test.myDelegate<object, object>>(methodCall, new ParameterExpression[] { instance, argument }).Compile();
    }

static void Main(string[] args)
    {
        Test t1 = new Test();

        var myFunctionMethodInfo = t1.GetType().GetMethod("myFunction");

        //this one works, the "out" string is set to "after" 
        string someString = "before";
        var compiledExactly = buildExactExpression(myFunctionMethodInfo);

        compiledExactly(t1, out someString);
        Console.WriteLine(someString);


        //the following doesn't return the expected output, the "out" parameter is not set,    "before" is printed
        object someObjectString = "before";
        var compiledObject = buildDesiredExpression(myFunctionMethodInfo);

        compiledObject(t1, out someObjectString);
        Console.WriteLine(someObjectString);
    }

For some objects in my prog, I am discovering their methods at runtime and do not know the parameter types ahead of time, so the conversion to "object" (buildDesiredExpression method) in the delegate that is returned is v.important. I would like to cache the returned "open" delegates so that I can run them with a minimal performance penalty during execution.

How can I fix the "buildDesiredExpression" method to make this work?

like image 987
bluefin Avatar asked Oct 29 '25 15:10

bluefin


1 Answers

What you can do is to create a local variable of the right type, let the called method set that, and then set the parameter:

ParameterExpression instance = Expression.Parameter(typeof(object));
ParameterExpression argument =
    Expression.Parameter(typeof(object).MakeByRefType());
ParameterExpression argumentVariable = Expression.Parameter(typeof(string));

var methodCall = Expression.Call(
                 Expression.Convert(instance, typeof(Test)),
                 methodInfo,
                 argumentVariable);

var block = Expression.Block(
    new[] { argumentVariable },
    methodCall, Expression.Assign(argument, argumentVariable));

return Expression.Lambda<Test.myDelegate<object, object>>(
    block, new[] { instance, argument }).Compile();
like image 93
svick Avatar answered Oct 31 '25 04:10

svick



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!