/请跳至更新,
将Net Framework 3.5(Mono)与C#7.0一起使用
我尝试创建动态方法,将其参数化,将它们装入对象数组并使用简单方法调用它。
然后在运行时我通过不安全的上下文替换方法。 (为了更好的API而做)
我创建了我想象的代码,并在LINQPad 5中发布了该代码。
void TestCall(string a, int b) {
NetworkInvoke(a, b);
}
void NetworkInvoke(params object[] parameters) {
}
在LINQPad中我得到了IL代码:
g__TestCall0_0:
IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: newarr System.Object
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldarg.0
IL_000A: stelem.ref
IL_000B: dup
IL_000C: ldc.i4.1
IL_000D: ldarg.1
IL_000E: box System.Int32
IL_0013: stelem.ref
IL_0014: call g__NetworkInvoke0_1
IL_0019: nop
IL_001A: ret
现在让我们在ILGenerator中重新创建它。 (我已经有了支持所有参数的代码,但它也不起作用。然后我创建了这个简单的静态代码,它也不起作用)
private MethodInfo CreateMethodFrom(MethodInfo method) {
var name = method.Name;
var attributes = method.Attributes;
var callConv = method.CallingConvention;
var returnType = method.ReturnType;
var parameters = new Type[method.GetParameters().Length];
for (int i = 0; i < parameters.Length; i++)
parameters[i] = method.GetParameters()[i].ParameterType;
Debug.Log("Creating dynamic method!");
var dynMethod = new DynamicMethod(name, attributes, callConv, returnType, parameters, GetType().BaseType, false);
for (int i = 0; i < parameters.Length; i++) {
dynMethod.DefineParameter(i, method.GetParameters()[i].Attributes, method.GetParameters()[i].Name);
}
var networkInvoker = GetType().BaseType.GetMethod("NetworkInvoke", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Log("Creating ILGenerator!");
ILGenerator il = dynMethod.GetILGenerator();
//ILCode(il, parameters, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Box, typeof(System.Int32));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
return dynMethod;
}
让我们调用我们的动态方法:
forEach { //Iterating over methods
MethodInfo ourCorrectMethodToBeReplaced = it; //This method is for a debug, its signature is: (string testString, int testInt)
var replacedMethod = CreateMethodFrom(ourCorrectMethodToBeReplaced);
string test = "test1";
int test2 = 69;
replacedMethod.Invoke(methodInstance, new object[] {test, test2});
}
当我调用它时,我收到InvalidProgramException
InvalidProgramException:(包装器动态方法)中的IL代码无效 SioFramework.Script:TestCall(string,int):IL_000b:stelem.ref
我试图删除引用 - (Stelem_Ref)然后我已经
了InvalidProgramException:(包装器动态方法)中的IL代码无效 SioFramework.Script:TestCall(string,int):IL_000d:ldarg.2
任何想法我做错了什么?谢谢你的任何提示。
更新,好吧,看起来我在这里发布了错误的代码。 (我昨天几乎整整都试图解决这个问题)
当我将ILCode重写为正确的(抱歉,我的错误)时
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Box, typeof(System.Int32));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
我有
InvalidProgramException:(包装器动态方法)中的IL代码无效 SioFramework.Script:TestCall(string,int):IL_0014:call
0x00000005
这是正确的错误。
更新2 我创建了Debug,在比较两种方法时看起来都相同。
private void DebugM(MethodInfo method) {
var builder = new StringBuilder();
builder.Append("Name: ").Append(method.Name);
builder.Append("\nParameters(").Append(method.GetParameters().Length).Append("): ");
foreach (var parameter in method.GetParameters()) {
builder.Append("(").Append(parameter.ParameterType.FullName).Append(")");
}
builder.Append("\nReturnType: ").Append(method.ReturnType.FullName);
builder.Append("\nMemberType: ").Append(method.MemberType);
builder.Append("\nCC: ").Append(method.CallingConvention);
builder.Append("\nModule: ").Append(method.Module.Name);
Debug.Log(builder.ToString());
}
名称:TestCall参数(2):( System.String)(System.Int32) ReturnType:System.Void MemberType:方法CC:标准,HasThis 模块:Assembly-CSharp.dll
我创建了与正在重建的内容完全相同的方法: 检测是否由于参数计数而导致 正在重建的方法:
public void TestCall(string testString, int testInt) {
Debug.Log("Called TestCAll with params: " + testInt + ", " + testInt);
}
致电方法:
internal void TestMethod(string a, int b) {
Debug.Log("Called TestMethod!!");
}
我试图用委托调用它:
public delegate void TestDelegate(String a, int b);
//In end of CreateMethodFrom
var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));
delTest("TESTA", 31321321);
结果相同:
InvalidProgramException:(包装器动态方法)中的IL代码无效 SioFramework.Script:TestCall(string,int):IL_0014:call
0x00000005
堆栈跟踪
System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:268)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:291)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:295)
System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type delegateType) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs:179)
SioFramework.Script.CreateMethodFrom (System.Reflection.MethodInfo method) (at Assets/SioFramework/Script.cs:167)
167:
var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));