使用ILGenerator.Emit调用另一个具有out参数的程序集中的方法

时间:2015-03-18 19:32:27

标签: c#

我需要使用ILGenerator在另一个程序集中使用out参数调用静态void方法。使用下面的代码,我能够调用该方法,并且它可以正确运行,但是即使本地变量已设置,也不会返回out参数。我不知道从哪里开始实际获取out参数值。

以下是我使用的代码:

    public delegate void DynamicMethodDelegate(object _target, params object[] _params); 

    public class DynamicDelegateStaticFactory
    {
        public static DynamicMethodDelegate CreateMethodCaller(MethodInfo method)
        {               
            ParameterInfo[] parameters = method.GetParameters();
            Type[] args = { typeof(object), typeof(object[]) };

            DynamicMethod dynam =
                new DynamicMethod
                    (
                          method.Name
                        , method.ReturnType
                        , args
                        , typeof(DynamicDelegateStaticFactory)
                        , true
                    );

            //Add parmeter attributes to the new method from the existing method
            for (int i = 0; i < parameters.Length; i++)
            {
                dynam.DefineParameter
                (
                    i,
                    parameters[i].Attributes,
                    parameters[i].Name
                );
            }

            ILGenerator il = dynam.GetILGenerator();

            // If method isn't static push target instance on top of stack.
            if (!method.IsStatic)
            {
                // Argument 0 of dynamic method is target instance.
                il.Emit(OpCodes.Ldarg_0);
            }

            // Lay out args array onto stack.

            LocalBuilder[] locals = new LocalBuilder[parameters.Length];
            for (int i = 0; i < parameters.Length; i++ )
            {
                //Push args array reference onto the stack, followed
                //by the current argument index (i). The Ldelem_Ref opcode
                //will resolve them to args[i].
                if (parameters[i].IsOut)
                {                       
                    locals[i] = il.DeclareLocal(parameters[i].ParameterType.GetElementType());
                    il.Emit(OpCodes.Ldloca, locals[locals.Length - 1].LocalIndex);
                }
                else
                {
                    // Argument 1 of dynamic method is argument array.
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldelem_Ref);
                }

                // If parameter [i] is a value type perform an unboxing.
                Type parameterType = parameters[i].ParameterType;
                if (parameterType.IsValueType)
                {
                    il.Emit(OpCodes.Unbox_Any, parameterType);
                }
            }

            if (method.IsFinal || !method.IsVirtual)
            {
                il.Emit(OpCodes.Call, method);
            }
            else
            {
                il.Emit(OpCodes.Callvirt, method);
            }

            if (method.ReturnType != typeof(void))
            {
                // If result is of value type it needs to be boxed
                if (method.ReturnType.IsValueType)
                {
                    il.Emit(OpCodes.Box, method.ReturnType);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldnull);
            }

            il.Emit(OpCodes.Ret);

            return (DynamicMethodDelegate)dynam.CreateDelegate(typeof(DynamicMethodDelegate));
        }

以下是我的称呼方式:

       //Create the method
        MethodInfo methodInfo = LoadXmlMethodInfo;
        IlHelper.MethodHelper.DynamicMethodDelegate delegate1 = MethodHelper.DynamicDelegateStaticFactory.CreateMethodCaller(methodInfo);

        //Call the method
        xmlReaderWarnings = null;
        object[] args = new object[] { NewTaxReturn, xml, xmlReaderWarnings };
        delegate1(null, args);

        //Extract the out parameter (xmlReaderWarnings)
        xmlReaderWarnings = (IList)args[2]; //args[2] is reader warning out parameter.

如何正确设置out参数以检索out值?

1 个答案:

答案 0 :(得分:3)

知道了!不得不将局部变量重新分配给我的参数数组中的out变量。最终代码

 public delegate void DynamicMethodDelegate(object _target, params object[] _params); 

    public class DynamicDelegateStaticFactory
    {
        public static DynamicMethodDelegate CreateMethodCaller(MethodInfo method)
        {               
            ParameterInfo[] parameters = method.GetParameters();
            Type[] args = { typeof(object), typeof(object[]) };

            DynamicMethod dynam =
                new DynamicMethod
                    (
                          method.Name
                        , method.ReturnType
                        , args
                        , typeof(DynamicDelegateStaticFactory)
                        , true
                    );

            //Add parmeter attributes to the new method from the existing method
            for (int i = 0; i < parameters.Length; i++)
            {
                dynam.DefineParameter
                (
                    i,
                    parameters[i].Attributes,
                    parameters[i].Name
                );
            }

            ILGenerator il = dynam.GetILGenerator();

            // If method isn't static push target instance on top of stack.
            if (!method.IsStatic)
            {
                // Argument 0 of dynamic method is target instance.
                il.Emit(OpCodes.Ldarg_0);
            }

            // Lay out args array onto stack.        
            LocalBuilder[] locals = new LocalBuilder[parameters.Length];
            List<LocalBuilder> outOrRefLocals = new List<LocalBuilder>();
            for (int i = 0; i < parameters.Length; i++ )
            {
                //Push args array reference onto the stack, followed
                //by the current argument index (i). The Ldelem_Ref opcode
                //will resolve them to args[i].
                if (!parameters[i].IsOut)
                {
                    // Argument 1 of dynamic method is argument array.
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldelem_Ref);
                }

                // If parameter [i] is a value type perform an unboxing.
                Type parameterType = parameters[i].ParameterType;
                if (parameterType.IsValueType)
                {
                    il.Emit(OpCodes.Unbox_Any, parameterType);
                }
            }

            //Create locals for out parameters
            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].IsOut)
                {
                    locals[i] = il.DeclareLocal(parameters[i].ParameterType.GetElementType());
                    il.Emit(OpCodes.Ldloca, locals[locals.Length - 1]);
                }
            }

            if (method.IsFinal || !method.IsVirtual)
            {
                il.Emit(OpCodes.Call, method);
            }
            else
            {
                il.Emit(OpCodes.Callvirt, method);
            }

            for (int idx = 0; idx < parameters.Length; ++idx)
            {
                if (parameters[idx].IsOut || parameters[idx].ParameterType.IsByRef)
                {
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldc_I4, idx);
                    il.Emit(OpCodes.Ldloc, locals[idx].LocalIndex);

                    if (parameters[idx].ParameterType.GetElementType().IsValueType)
                        il.Emit(OpCodes.Box, parameters[idx].ParameterType.GetElementType());

                    il.Emit(OpCodes.Stelem_Ref);
                }
            }

            if (method.ReturnType != typeof(void))
            {
                // If result is of value type it needs to be boxed
                if (method.ReturnType.IsValueType)
                {
                    il.Emit(OpCodes.Box, method.ReturnType);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldnull);
            }

            il.Emit(OpCodes.Ret);

            return (DynamicMethodDelegate)dynam.CreateDelegate(typeof(DynamicMethodDelegate));
        }

        private static void EmitUnboxOrCast(ILGenerator il, Type typeOnStack)
        {
            if (typeOnStack.IsValueType || typeOnStack.IsGenericParameter)
            {
                il.Emit(OpCodes.Unbox_Any, typeOnStack);
            }
            else
            {
                il.Emit(OpCodes.Castclass, typeOnStack);
            }
        }