动态创建方法并执行 [英] Creating method dynamically, and executing it

查看:82
本文介绍了动态创建方法并执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我想在C#中定义几个 static 方法,并从其中一种方法(在客户端上)在运行时选择一种方法,将IL代码生成为字节数组,然后将字节数组通过网络发送到另一台计算机(服务器),在从该字节重新生成IL代码后应在该计算机上执行该字节数组数组。

I want to define few static methods in C# , and generate IL code as byte array, from one of these methods, selected at runtime (on client), and send the byte array over network to another machine (server) where it should be executed after re-generating the IL code from the byte array.

我的尝试: POC

public static class Experiment
{
    public static int Multiply(int a, int b)
    {
        Console.WriteLine("Arguments ({0}, {1})", a, b);
        return a * b;
    }
}

然后我得到方法主体的IL代码,例如:

And then I get the IL code of the method body, as:

BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
MethodInfo meth = typeof(Experiment).GetMethod("Multiply", flags);
byte[] il = meth.GetMethodBody().GetILAsByteArray();

到目前为止,我还没有动态创建任何东西。但是我将IL代码作为字节数组,我想创建一个程序集,然后在其中创建一个模块,然后创建一个类型,然后再创建一个方法-所有这些都是动态的。在创建动态创建的方法的方法主体时,我使用上面代码中通过反射获得的IL代码。

So far I didn't create anything dynamically. But I've IL code as byte array, and I want to create an assembly, then a module in it, then a type, then a method - all dynamically. When creating the method body of the dynamically created method, I use the IL code which I got using reflection in the above code.

代码生成代码如下:

AppDomain domain = AppDomain.CurrentDomain;
AssemblyName aname = new AssemblyName("MyDLL");
AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly(
                                               aname, 
                                               AssemblyBuilderAccess.Run);

ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("MainModule");

TypeBuilder tb = modBuilder.DefineType("MyType", 
                            TypeAttributes.Public | TypeAttributes.Class);

MethodBuilder mb = tb.DefineMethod("MyMethod", 
     MethodAttributes.Static | MethodAttributes.Public, 
     CallingConventions.Standard,
     typeof(int),                          // Return type
     new[] { typeof(int), typeof(int) });  // Parameter types

mb.DefineParameter(1, ParameterAttributes.None, "value1");  // Assign name 
mb.DefineParameter(2, ParameterAttributes.None, "value2");  // Assign name 

//using the IL code to generate the method body
mb.CreateMethodBody(il, il.Count()); 

Type realType = tb.CreateType();

var meth = realType.GetMethod("MyMethod");
try
{
    object result = meth.Invoke(null, new object[] { 10, 9878 });
    Console.WriteLine(result);  //should print 98780 (i.e 10 * 9878)
}
catch (Exception e)
{
    Console.WriteLine(e.ToString());
}

但不要打印 98780 在输出窗口上,它将引发异常,

But instead of printing 98780 on the output window, it throws an exception saying,


System.Reflection.TargetInvocationException:异常已由目标计算机的目标抛出。调用。 ---> System.TypeLoadException:无法从程序集 MyDLL,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null中加载类型 Invalid_Token.0x0100001E。

    在MyType.MyMethod(Int32 value1,Int32 value2)
[...]

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeLoadException: Could not load type 'Invalid_Token.0x0100001E' from assembly 'MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
     at MyType.MyMethod(Int32 value1, Int32 value2) [...]

请帮助我弄清楚原因错误以及如何解决。

Please help me figuring out the cause of the error, and how to fix it.

推荐答案

在任意程序集上运行ildasm.exe。使用查看+显示令牌值,并查看一些反汇编的代码。您会看到,IL通过数字包含对其他方法和变量的引用。这个数字是程序集的元数据表的索引。

Run ildasm.exe on an arbitrary assembly. Use View + Show token values and look at some disassembled code. You'll see that the IL contains references to other methods and variables through a number. The number is an index into the metadata tables for an assembly.

也许现在您看到了问题,除非将IL从一个程序集移植到另一个程序集,否则您无法移植它程序集具有相同的元数据。或者,除非用与目标程序集的元数据匹配的值替换IL中的元数据标记值。当然,这是完全不切实际的,您最终将不得不复制程序集。也可以复制该程序集。或就此而言,不妨使用原始程序集中的现有IL。

Perhaps you now see the problem, you cannot transplant a chunk of IL from one assembly to another unless that target assembly has the same metadata. Or unless you replace the metadata token values in the IL with values that match the metadata of the target assembly. This is wildly impractical of course, you essentially end up duplicating the assembly. Might as well make a copy of the assembly. Or for that matter, might as well use the existing IL in the original assembly.

您需要仔细考虑一下,目前尚不清楚您实际尝试做什么完成。 System.CodeDom和Reflection.Emit命名空间可用于动态生成代码。

You need to think this through a bit, it is pretty unclear what you actually try to accomplish. The System.CodeDom and Reflection.Emit namespaces are available to dynamically generate code.

这篇关于动态创建方法并执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆