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

6 thoughts on “Overcoming problems with MethodInfo.Invoke of methods with by-reference value type arguments”

  1. Forgot the code :)

    static void Main()
    {
    MethodInfo mi = typeof(Program).GetMethod(“TestMethod”);
    var callback = (OneRefParameterCallback)Delegate.CreateDelegate(typeof(OneRefParameterCallback), mi);
    int i = 0;
    callback(ref i);
    Debug.Assert(i == testValue);
    }

  2. It’s not only nicer to use with a delegate but also alot more performant. All the runtime type-checks aren’t required anymore. This way you can increase reflection performance near to normal delegate invocation performance (which is really great)!

  3. I am trying to use reflection to perform calculations on properties that are of type ulong. The problem is that even caching the propetyInfo’s is not sufficient, since the GetValue and SetValue calls return an object thus the overhead of boxing and unboxing. Is there a way to generate IL instructions that will avoid the unnecessary boxing/unboxing of the GetValue/SetValue Invocations?

  4. Friends,

    I have an instance method as follows.

    public List executeService(ref Object RoleDetailresponse)

    How do I create the delegate to invoke the same?

    Thanks in Advance.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>