C# - Reflection.Emit: вернуть результат вызванного метода - PullRequest
0 голосов
/ 27 мая 2020

В DynamicMethod я пытаюсь вызвать метод, который хочет, чтобы массив объектов возвращал длину данного массива. В настоящее время мой метод, который должен вызываться из DynamicMethod, выглядит следующим образом:

public static int Test(Object[] args)
{
    Console.WriteLine(args.Length);
    return args.Length;
}

Процесс создания DynamicMethod выглядит следующим образом:

(Создание массива Object выполняется заимствовано из следующего SO answer )

public static DynamicMethod GetDM()
{ 
    var returnType = typeof(int);
    var paramTypes = new Type[]{typeof(string), typeof(bool)};

    var method = new DynamicMethod(
        "",
        returnType,
        paramTypes,
        false
    );
    var il = method.GetILGenerator();

    // Save parameters in an object array
    il.Emit(OpCodes.Ldc_I4_S, paramTypes.Length);
    il.Emit(OpCodes.Newarr, typeof(Object));
    il.Emit(OpCodes.Dup);

    for (int i = 0; i < paramTypes.Length; i++)
    {
        Type type = paramTypes[i];

        il.Emit(OpCodes.Ldc_I4, i);
        il.Emit(OpCodes.Ldarg, i);
        if (type.IsValueType) { il.Emit(OpCodes.Box, type); }
        il.Emit(OpCodes.Stelem_Ref);
        il.Emit(OpCodes.Dup);
    }

    // Call method and get the length of the array
    // How do I return the result of the called method?
    var callMethod = typeof(Program).GetMethod("Test", (BindingFlags)(-1));
    il.Emit(OpCodes.Call, callMethod);

    il.Emit(OpCodes.Ret);

    return method;
}

С помощью следующего метода я проверяю функциональность:

public static void Main(string[] args)
{
    var method = GetDM();
    var result = method.Invoke(null, new Object[]{"Test 1234", true});
    Console.WriteLine(result); // Should be 2
}

Когда я запускаю основной метод, я получаю System.Reflection.TargetInvocationException. Может ли кто-нибудь помочь мне вернуть значение, которое было возвращено вызываемым методом? Вот ссылка на dotnetfiddle, чтобы увидеть мою проблему в действии.

1 Ответ

1 голос
/ 27 мая 2020

После for l oop у вас есть созданный массив объектов дважды в стеке (из-за вызовов Dup). Только одна из этих ссылок на массив используется Call, поэтому в конце метода у вас будет одна дополнительная ссылка на массив в стеке.

Чтобы исправить это, удалите первый Dup и переместите вторую в голова л oop корпус:

public static DynamicMethod GetDM() {  
    var returnType = typeof(int);
    var paramTypes = new Type[]{typeof(string), typeof(bool)};

    var method = new DynamicMethod(
        "",
        returnType,
        paramTypes,
        false
    );
    var il = method.GetILGenerator();

    // Save parameters in an object array
    il.Emit(OpCodes.Ldc_I4_S, paramTypes.Length);
    il.Emit(OpCodes.Newarr, typeof(Object));

    for (int i = 0; i < paramTypes.Length; i++)
    {
        Type type = paramTypes[i];

        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldc_I4, i);
        il.Emit(OpCodes.Ldarg, i);
        if (type.IsValueType) { il.Emit(OpCodes.Box, type); }
        il.Emit(OpCodes.Stelem_Ref);
    }

    // Call method and get the length of the array
    // How do I return the result of the called method?
    var callMethod = typeof(Program).GetMethod("Test", (BindingFlags)(-1));
    il.Emit(OpCodes.Call, callMethod);

    il.Emit(OpCodes.Ret);

    return method;
}
...