sequência parâmetro estranho usando Reflection.Emit
-
22-07-2019 - |
Pergunta
Eu tenho estado a olhar para Reflection.Emit recentemente. Eu escrevi um programa simples que gera um DynamicMethod que simples chama outro método com os mesmos 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);
}
}
Quando eu executar este exemplo que eu esperaria que a saída 1,2,3,4,5,6 no entanto, saídas 2,3,4,5,6,1
Eu não estou muito certo porque ... Se vocês sabe de qualquer bons recursos para usar Reflection.Emit você poderia me apontar nessa direção. Tenho vindo a utilizar refletor com o Emit AddIn.
Felicidades
Rohan
Solução
O problema que você tem é que você está invocando um método dinâmico, não um estático. Seu método gerado não tem uma referência para a instância da classe Programa.
Além disso, note que você está empurrando 7 parâmetros para a pilha para um método 6 parâmetro. O primeiro parâmetro deve ser uma referência para o objeto que você está invocando o método on.
O comportamento estranho que você está vendo pode ser devido a não haver um parâmetro de índice 6, e envolvendo em torno de volta para o início da matriz de parâmetro.
Consulte "Como definir e executar Métodos dinâmicos" na ajuda VS
.Você pode fazê-lo funcionar, aceitando um parâmetro de objeto em sua chamada de método, ou torná-lo estático:
delegado pública TestHandler vazio (instância de objecto, int a, b int, int c, 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);
}
Outras dicas
Para fazer o trabalho você deve
- Faça
Question
um métodostatic
- comentário
gen.Emit(OpCodes.Ldarg_S,6);
- modificar
MethodInfo method1 = ...
conformidade
Um "cheiro" é que parar na depuração no método Question
você não pode avaliar a referência this
. E isso não deve ser por um método de instância ...; -)
Edit: Ops. Acabo de ver a resposta de Robert Wagner, que é muito melhor explicou que o meu. Pronto para cancelar meu post se necessário ...: -)