There are some definations:
public class Message
{
public SayType Say(string name)
{
Console.Write("Hello," name );
return SayType.Name;
}
public SayType Say(string name,string haha)
{
Console.Write("Hello," name );
return SayType.Name;
}
}
public enum SayType
{
Name
}
SayDelegate:
public delegate SayType SayDelegate(object message,params object[] o);
And I want to create two dynamic of the two function in class Message. The first:
DynamicMethod dynamicMethod =
new DynamicMethod("Say",typeof(SayType),new Type[]{typeof(object),typeof(object[])});
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg,0);
il.Emit(OpCodes.Ldarg,1);
il.Emit(OpCodes.Ldc_I4,0);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Callvirt,typeof(Message).GetMethods()[0]);
//[0] refers to the first method
il.Emit(OpCodes.Ret);
System.Delegate delegates = dynamicMethod.CreateDelegate(typeof(SayDelegate));
delegates.DynamicInvoke(new Message(), new object[]{"123123"});
It can work normally.
However, if I create the second delegate like this:
DynamicMethod dynamicMethod =
new DynamicMethod("Say",typeof(SayType),new Type[]{typeof(object),typeof(object[])});
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg,0);
il.Emit(OpCodes.Ldarg,1);
il.Emit(OpCodes.Ldc_I4,0);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Ldc_I4,1);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Callvirt,typeof(Message).GetMethods()[1]);
//[1] refers to the second method
il.Emit(OpCodes.Ret);
System.Delegate delegates = dynamicMethod.CreateDelegate(typeof(SayDelegate));
delegates.DynamicInvoke(new Message(), new object[]{"123123","123123"});
It will have some complains:
---> System.InvalidProgramException: Common Language Runtime detected an invalid program.
at Say(Object , Object[] )
--- End of inner exception stack trace ---
CodePudding user response:
You missed a ldarg.1
:
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldarg, 1);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldelem_Ref);
il.Emit(OpCodes.Ldarg, 1); // <<== this one
il.Emit(OpCodes.Ldc_I4, 1);
il.Emit(OpCodes.Ldelem_Ref);
The trick is to compile and decompile what you want, and look at the IL; in this case we see:
IL_0000: ldarg.0
IL_0001: castclass Message
IL_0006: ldarg.1
IL_0007: ldc.i4.0
IL_0008: ldelem.ref
IL_0009: castclass [System.Runtime]System.String
IL_000e: ldarg.1
IL_000f: ldc.i4.1
IL_0010: ldelem.ref
IL_0011: castclass [System.Runtime]System.String
IL_0016: callvirt instance valuetype SayType Message::Say(string, string)
IL_001b: ret
and compare that to your opcodes (noting that you're using unsafe coerce, which ... I'm not going to advocate for, but I also can't stop you from doing)