Overcoming problems with MethodInfo.Invoke of methods with by-reference value type arguments

I ran into an interesting problem on the Forums recently.  Basically, when you use MethodInfo.Invoke to invoke a method with by-reference value type arguments you can’t have the invoked method update a variable/argument.  The problem is, when you invoke the method the parameter is passed to the MethodInfo.Invoke via an object array.  Since we’re dealing with a value type, the original value type is boxed and the invoked method actually updates the array element, not the original object (as it would with reference types).  For example:



using System;


using System.Reflection.Emit;


using System.Reflection;


using System.Diagnostics;


 


namespace InvokeTesting


{


    class Program


    {


        private const int testValue = 10;


        public static void TestMethod(ref int i)


        {


            i = testValue;


        }


        static void Main(string[] args)


        {


            MethodInfo methodInfo = typeof (Program).GetMethod(“TestMethod”, BindingFlags.Static | BindingFlags.Public);


            int i = 0;


            object[] parameters = new object[] {i};


            methodInfo.Invoke(null, parameters);


            // original variable isn’t updated


            Debug.Assert(i == 0);


            // array element is updated:


            Debug.Assert((int)parameters[0] == testValue);


 


            // copy updated value to original variable


            i = (int)parameters[0];


 


            Debug.Assert(i == testValue);


        }


    }


}


Of course, the only “workaround” is to get the new value out of the array.


Since essentially we’re implementing a run-time method invocation, a “better” way would be to create a dynamic method to make the call.  A dynamic method is a method created at run-time (whose body is generated through ILGenerator.Emit et al) and invoked via a delegate.  For example:



using System;


using System.Reflection.Emit;


using System.Reflection;


using System.Diagnostics;


 


namespace EmitTesting


{


    // delegate for void method that takes one reference parameter


    public delegate void OneRefParameterCallback<T>(ref T value);


 


    class Program


    {


        private const int testValue = 10;


        public static void TestMethod(ref int i)


        {


            i = testValue;


        }


        static void Main(string[] args)


        {


            // create a dynamic method object with arbitrary name “Caller”, void return (null), and one parameter “ref int”


            DynamicMethod caller = new DynamicMethod(“Caller”, null, new Type[] { typeof(int).MakeByRefType() }, typeof(Program).Module);


 


            ILGenerator ilGenerator = caller.GetILGenerator();


 


            // emit ldarg.0


            ilGenerator.Emit(OpCodes.Ldarg_0);


 


            // emit call void EmitTesting.Program::TestMethod(int32&)


            MethodInfo mi = typeof(Program).GetMethod(“TestMethod”, BindingFlags.Static | BindingFlags.Public, null,


                                                    new Type[] { typeof(int).MakeByRefType() }, null);


            ilGenerator.Emit(OpCodes.Call, mi);


 


            // emit ret


            ilGenerator.Emit(OpCodes.Ret);


 


            OneRefParameterCallback<int> callback =


                (OneRefParameterCallback<int>)caller.CreateDelegate(typeof(OneRefParameterCallback<int>));


 


            // call our emitted code with a reference parameter


            int i = 0;


            callback(ref i);


            Debug.Assert(i == testValue);


        }


    }


}


In this example, the method invocation directly updates a value type variable.  This could be made more re-usable to refactoring the dynamic method creation code into it’s own method.

kick it on DotNetKicks.com