我试图在运行时使用反射创建方法的副本。
我有以下代码。
public static R CopyMethod<T, R>(Func<T, R> f, T t)
{
AppDomain currentDom = Thread.GetDomain();
AssemblyName asm = new AssemblyName();
asm.Name = "DynamicAssembly";
AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run);
ModuleBuilder mbl = abl.DefineDynamicModule("Module");
TypeBuilder tbl = mbl.DefineType("Type");
var info = f.GetMethodInfo();
MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray());
byte[] il = f.Method.GetMethodBody().GetILAsByteArray();
mtbl.CreateMethodBody(il, il.Length);
Type type = tbl.CreateType();
Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>;
return method(t);
}
最后一行抛出异常并显示消息:
公共语言运行时检测到无效程序。
还有另一种方法可以做到这一点吗?我更希望能够获取方法的解析树,而不是直接使用 IL。
EDIT 1:
我正在使用以下功能进行测试。
public static int Fib(int n)
{
/*if (n < 2)
return 1;
return Fib(n - 1) + Fib(n - 2);*/
return n;
}
使用以下行进行测试。
int x = Copy.CopyMethod(Copy.Fib, 10);
EDIT 2:
罗布的回答有助于解决上述问题。然而,当使用Fib()
稍微复杂一点的方法(例如注释的斐波那契方法),程序会崩溃并显示以下消息。
未找到索引。 (HRESULT 异常:0x80131124)
EDIT 3:
我尝试了评论中的一些建议,但元数据令牌无法位于动态程序集中。
public static R CopyMethod<T, R>(Func<T, R> f, T t)
{
AppDomain currentDom = Thread.GetDomain();
AssemblyName asm = new AssemblyName("DynamicAssembly");
AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run);
ModuleBuilder mbl = abl.DefineDynamicModule("Module");
TypeBuilder tbl = mbl.DefineType("Type");
MethodInfo info = f.GetMethodInfo();
MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray());
MethodBody mb = f.Method.GetMethodBody();
byte[] il = mb.GetILAsByteArray();
OpCode[] opCodes = GetOpCodes(il);
Globals.LoadOpCodes();
MethodBodyReader mbr = new MethodBodyReader(info);
string code = mbr.GetBodyCode();
Console.WriteLine(code);
ILGenerator ilg = mtbl.GetILGenerator();
ilg.DeclareLocal(typeof(int[]));
ilg.DeclareLocal(typeof(int));
for (int i = 0; i < opCodes.Length; ++i)
{
if (opCodes[i].OperandType == OperandType.InlineType)
{
int token;
Type tp = info.Module.ResolveType(token = BitConverter.ToInt32(il, i + 1), info.DeclaringType.GetGenericArguments(), info.GetGenericArguments());
ilg.Emit(opCodes[i], tp.MetadataToken);
i += 4;
continue;
}
if (opCodes[i].FlowControl == FlowControl.Call)
{
int token;
MethodBase mi = info.Module.ResolveMethod(token = BitConverter.ToInt32(il, i + 1));
ilg.Emit(opCodes[i], mi.MetadataToken);
i += 4;
continue;
}
ilg.Emit(opCodes[i]);
}
Type type = tbl.CreateType();
Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>;
return method(t);
}
以下也不起作用。
var sigHelp = SignatureHelper.GetLocalVarSigHelper(mtbl.Module);
mtbl.SetMethodBody(il, mb.MaxStackSize, sigHelp.GetSignature(), null, new int[] { 3 });
我可以通过以下方式更改元数据令牌来修复递归函数调用(我意识到这并非在所有情况下都有效,但我试图让它以某种方式工作)。
if (opCodes[i].FlowControl == FlowControl.Call)
{
ilg.Emit(opCodes[i], mtbl);
i += 4;
}
我可以使用相关问题的答案中建议的方法构建动态方法:引用 IL 构造方法中的集合 https://stackoverflow.com/questions/34951233/reference-a-collection-from-il-constructed-method。然而,当尝试在这里做同样的事情时,它失败了。