Extraña secuencia de parámetros usando Reflection.Emit
-
22-07-2019 - |
Pregunta
He estado mirando Reflection.Emit recientemente. Escribí un programa simple que genera un DynamicMethod que simplemente llama a otro método con los mismos parámetros
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Test();
}
public delegate void TestHandler(int a, int b, int c, int d, int e, int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question",BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,null,new Type[]{typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32)},null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
}
Cuando ejecuto este ejemplo, esperaría que produjera 1,2,3,4,5,6, sin embargo, genera 2,3,4,5,6,1
No estoy muy seguro de por qué ... Si ustedes saben de algún buen recurso para usar Reflection.Emit, ¿podrían señalarme en esa dirección? He estado usando Reflector con el complemento Emit.
Saludos
Rohan
Solución
El problema que tiene es que está invocando un método dinámico, no estático. Su método generado no tiene una referencia a la instancia de la clase Program.
También tenga en cuenta que está introduciendo 7 parámetros en la pila para un método de 6 parámetros. El primer parámetro debe ser una referencia al objeto sobre el que está invocando el método.
El comportamiento extraño que está viendo puede deberse a que no hay un parámetro del índice 6, y que se remonta al inicio de la matriz de parámetros.
Consulte " Cómo: Definir y ejecutar métodos dinámicos " en la ayuda de VS.
Puede hacerlo funcionar aceptando un parámetro de objeto en su llamada al método o haciéndolo estático:
público delegado void TestHandler (instancia de objeto, int a, int b, int c, int d, int e, int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(object), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(this, 1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
Otros consejos
Para que funcione, deberías
- convierte la
Pregunta
en un métodoestático
- comentario
gen.Emit(OpCodes.Ldarg_S,6);
- modificar
MethodInfo method1 = ...
en consecuencia
Uno " olor " es que al detener la depuración en el método Question
no se puede evaluar la referencia this
. Y esto no debería ser para un método de instancia ... ;-)
Editar: Ops. Acabo de ver la respuesta de Robert Wagner, que está mucho mejor explicada que la mía. Listo para cancelar mi publicación si es necesario ... :-)