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);
outrather thanref, the same problem applies and the same answer solves, unless I'm missing a further difference.typeof(int).MakeByRefType()does not help, if that's what you expected the answer.Expression.Dynamic?