I reproduced your problem and found a few potential issues with the code in question. To explain, I'll start with how I interpreted what you are actually trying to do, which is run the following code:
dynamic program = ...;
program.RefTest(ref myInt);
Note that the above code works fine and runs the method.
A few things to note here:
- The
program
variable is a constant.
- The method returns
void
- We should be able to pass in any integer by reference.
Some things in your question that do not align with this are:
- Your
Expression.Dynamic
call tells the method to return object
. Instead it should return typeof(void)
.
- Your
Expression.Lambda
call specifies the delegate type as an Action<int>
. The parameter should be of type ref int
. To solve this there should be an Action
that has ref
parameters instead. delegate void RefAction<T>(ref T arg1)
This has brought me to the following code:
Expression programExpr = Expression.Constant(program);
var binder = Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded | CSharpBinderFlags.InvokeSimpleName, "Bla", null, typeof (Program),
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsRef, null) });
var intParam = Expression.Parameter(typeof (int).MakeByRefType(), "x");
Expression methodCall = Expression.Dynamic(binder, typeof (void), programExpr, intParam);
var expr = Expression.Lambda<RefAction<int>>(methodCall, intParam).Compile();
expr(ref myInt);
However, I get the same exception as you:
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in
System.Core.dll
Additional information: Cannot convert type 'int' to 'ref int'
This leads me to believe there is some framework bug that causes the RuntimeBinder
to incorrectly handle ref
parameters for dynamic invocation, or that the Expression.Dynamic
code incorrectly assigns by value parameters where ref
parameters should be used.
Note that I was able to reproduce the issue for out
parameters as well, yielding the exact same result.
For you situation however, you already appear to know so much about the types you are going to invoke, and even create a strongly typed delegate to invoke. In that case I would go with the following:
var programExpr = Expression.Constant(program);
var intParam = Expression.Parameter(typeof(int).MakeByRefType(), "x");
var methodCall = Expression.Call(programExpr, typeof (Program)
.GetMethod("RefTest", BindingFlags.NonPublic | BindingFlags.Instance), intParam);
var expr = Expression.Lambda<RefAction<int>>(methodCall, intParam).Compile();
expr(ref myInt);