1

Trying to invoke a sample method taking a ref parameter:

public void RefTest(ref int i)
{
    Console.WriteLine(i);
    i = 18;
}

Utilizing the DLR:

var prog = new Program();
var binder = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
    CSharpBinderFlags.ResultDiscarded,
    "RefTest", null, typeof(Program),
    new CSharpArgumentInfo[]{
        CSharpArgumentInfo.Create(0,null),
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsRef,null)
    }
);
ParameterExpression p = Expression.Parameter(typeof(int));
Expression dyn = Expression.Dynamic(binder, typeof(object), Expression.Constant(prog), p);
var lam = Expression.Lambda<Action<int>>(dyn, p).Compile();
lam(9); //RuntimeBinderException

However, the code fails with RuntimeBinderException that it can't convert int to ref int. How to fix it?

I am trying to mimic the following code:

dynamic prog = new Program();
Action<int> lam = i => prog.RefTest(ref i);
lam(9);

I have to use DLR instead of reflection, because the supplied object (prog) may be dynamic.

7
  • While stackoverflow.com/questions/3146317/… is about out rather than ref, the same problem applies and the same answer solves, unless I'm missing a further difference. Commented Apr 24, 2015 at 16:39
  • @Jon Wrong. This uses the DLR, not reflection. Also setting the parameter type to typeof(int).MakeByRefType() does not help, if that's what you expected the answer. Commented Apr 24, 2015 at 16:43
  • @IllidanS4 the other example uses Expression trees in the same way you do. Are you mixing up DLR with runtime creation of expressions? Commented Apr 24, 2015 at 17:00
  • @Bas Duh, I am - don't you see Expression.Dynamic? Commented Apr 24, 2015 at 17:11
  • @IllidanS4 just making sure this is intended Commented Apr 24, 2015 at 17:13

2 Answers 2

1

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);
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for you effort, but I need to use the DLR in case the object was dynamic (ref and out parameters are supported on dynamic objects).
@IllidanS4 That's why I mentioned the likelyhood that you can't do it because of a runtime bug. I hope you solve it but it feels like something that cannot be solved.
0

If I'm not wrong, Dynamic doesn't support ref/out parameters. Refer Alexandra Rusina's answer in the linked thread and here also.

Try the following (which won't compile):

dynamic value = 5;
RefTest(ref value);

Simply dynamic don't support ref/out. So obviously trying the same with expressions also won't work.

2 Comments

How I read the question is that the parameter is strongly typed. It's just dynamic invocation that the OP is asking for...
@Bas You may be right. Not sure I'm missing the actual question. I believe this is a XY question though. After reading your answer I believe, ref and dynmaic doesn't work together well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.