簡體   English   中英

IL使用Reflection.Emit調用params object []參數的方法

[英]IL Calling a method with params object[] arguments using Reflection.Emit

我正在編寫一個需要后續類型構建的庫。 庫使用平台.Net core 2.0

我使用Reflection.Emit生成某些類型的問題

public class GeneratedA : A, IA
{
    public void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4, params object[] otherArgs)
    {
        DoClass(arg0, arg1, arg2, arg3, arg4, otherArgs);
    }
}

對於這些類型:

public interface IA
{
    void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4,  params object[] otherArgs);
}
public class A
{
    public void DoClass(params object[] args)
    {
    }
}

樣本IL代碼:

class Program
{
    public static class Generator
    {
        public static T Create<T>()
            where T : class
        {
            AssemblyName aName = new AssemblyName("DynamicAssembly");
            AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
            ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);

            var interfaceType = typeof(T);
            var interfaceMethod = interfaceType.GetMethod("DoInterface");
            var interfaceMethodArgs = interfaceMethod.GetParameters().Select(x => x.ParameterType).ToArray();
            var classType = typeof(A);
            var classMethod = classType.GetMethod("DoClass");
            var returnType = typeof(void);
            var baseType = typeof(object);
            var baseConstructor = baseType.GetConstructor(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance, null, Type.EmptyTypes, null);


            TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, baseType);


            ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
            ILGenerator ctorIL = ctor.GetILGenerator();
            ctorIL.Emit(OpCodes.Ldarg_0);
            ctorIL.Emit(OpCodes.Call, baseConstructor);
            ctorIL.Emit(OpCodes.Nop);
            ctorIL.Emit(OpCodes.Nop);
            ctorIL.Emit(OpCodes.Ret);


            tb.AddInterfaceImplementation(interfaceType);

            MethodBuilder mbIM = tb.DefineMethod(interfaceType.Name + "." + interfaceMethod.Name,
                MethodAttributes.Private | MethodAttributes.HideBySig |
                MethodAttributes.NewSlot | MethodAttributes.Virtual |
                MethodAttributes.Final,
                returnType,
                interfaceMethodArgs);
            ILGenerator genIM = mbIM.GetILGenerator();
            // ToDo
            genIM.Emit(OpCodes.Call, classMethod);
            genIM.Emit(OpCodes.Ret);


            tb.DefineMethodOverride(mbIM, interfaceMethod);

            Type t = tb.CreateType();

            return Activator.CreateInstance(t) as T;
        }

    }

    static void Main(string[] args)
    {
        IA a;
        a = new GeneratedA();
        a.DoInterface("0", true, 2, 3, new List<float>() { 4 }, "5", 6);
        a = Generator.Create<IA>();
        a.DoInterface("0", true, 2, 3, new List<float>() { 4 }, "5", 6);
    }
}

每當我嘗試填寫評論“ToDo”時,我都會收到錯誤“Common Language Runtime檢測到無效程序”。

我請求幫助調用方法DoClass。

謝謝

要調用DoClass方法,您需要提供參數,只需Call classMethod就行了。

第一個參數當然是“這個”參考:

genIM.Emit(OpCodes.Ldarg_0);

第二個參數是對象數組。 params是編譯器功能,如果您自己構建代碼 - 您必須將其視為params不在那里。 我的意思是 - 同時

 DoClass();

在代碼中編寫它是合法的 - 這被編譯為:

DoClass(new object[0]);

因此,當您發出此調用時 - 您應該始終提供對象數組參數,您不能省略它。

要將對象數組推送到堆棧:

// push array length (0, for example) to stack
genIM.Emit(OpCodes.Ldc_I4_0); 
// push new array with length given by the above value (0)
genIM.Emit(OpCodes.Newarr, typeof(object)); 

此時您的代碼將編譯並運行正常。 這類似於:

public class GeneratedA : A, IA
{
    public void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4, params object[] otherArgs)
    {
        DoClass();
    }
}

如果要傳遞DoInterface所有參數, DoInterface需要更多工作。 我將提供幾個例子。 傳遞第一個參數( string arg0 ):

genIM.Emit(OpCodes.Dup);
// push index to store next element at (0)
genIM.Emit(OpCodes.Ldc_I4_0);
// push first argument (arg0 of DoInterface) to stack
genIM.Emit(OpCodes.Ldarg_1);
// store element in array at given index (yourArguments[0] = arg0)
genIM.Emit(OpCodes.Stelem_Ref);

傳遞第二個參數:

genIM.Emit(OpCodes.Dup);
// push index to store next element at (1)
genIM.Emit(OpCodes.Ldc_I4_1);
// push arg2
genIM.Emit(OpCodes.Ldarg_2);
// box, because boolean is value type, and you store it in object array
genIM.Emit(OpCodes.Box, typeof(bool));
// store in array (yourArguments[1] = (object) arg2
genIM.Emit(OpCodes.Stelem_Ref);

等等。

當您將參數推入數組時,不要忘記更改其長度以反映參數的數量:

// push array length - 6
genIM.Emit(OpCodes.Ldc_I4_6); 
// push new array with length given by the above value (6)
genIM.Emit(OpCodes.Newarr, typeof(object)); 

另請注意,您可能想要更改:

TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, baseType);

TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, classType);

或者只是更改為baseType = typeof(A) ,因為您要從A繼承生成的類,而不是從object繼承。

使用前2個args發出調用的完整代碼:

ILGenerator genIM = mbIM.GetILGenerator();            
genIM.Emit(OpCodes.Ldarg_0);   
genIM.Emit(OpCodes.Ldc_I4_2);
genIM.Emit(OpCodes.Newarr, typeof(object));

genIM.Emit(OpCodes.Dup);
genIM.Emit(OpCodes.Ldc_I4_0);
genIM.Emit(OpCodes.Ldarg_1);
genIM.Emit(OpCodes.Stelem_Ref);

genIM.Emit(OpCodes.Dup);
genIM.Emit(OpCodes.Ldc_I4_1);
genIM.Emit(OpCodes.Ldarg_2);
genIM.Emit(OpCodes.Box, typeof(bool));
genIM.Emit(OpCodes.Stelem_Ref);

genIM.Emit(OpCodes.Call, classMethod);
genIM.Emit(OpCodes.Ret);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM