如何使用代码生成来动态创建 C# 方法? [英] How to use code generation to dynamically create C# methods?

查看:14
本文介绍了如何使用代码生成来动态创建 C# 方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了在 C 中定义一个可由 Lua 调用的方法,它必须匹配给定的签名并使用 Lua API 来检索参数并返回结果.我正在编写 Lua 的 C# 包装器,并且我对能够调用任意 C# 方法而不使它们遵循这些约定很感兴趣.当用 D 之类的东西包装时,人们可能会使用模板系统为任何给定的方法动态创建此粘合代码.我想这在 C# 中也是可能的,但是通过使用动态代码生成.

In order to define a method in C that is callable by Lua it has to match a given signature and use the Lua API to retrieve parameters and return results. I'm writing a C# wrapper of Lua and I'm interested in being able to call arbitrary C# methods without making them follow these conventions. When wrapping in something like D, one might use the template system to dynamically create this glue code for any given method. I was thinking this might be possible as well in C#, but by using dynamic code generation.

C API 看起来像这样,生成的代码将通过我的库的较低级别部分操作它,该部分 P/Invokes Lua C 库.

The C API looks something like this, and the generated code would manipulate this through a lower level part of my library which P/Invokes the Lua C library.

static int foo (lua_State *L)
{
    int n = lua_gettop(L);    /* number of arguments */
    lua_Number sum = 0;
    int i;
    for (i = 1; i <= n; i++)
    {
        if (!lua_isnumber(L, i)) 
        {
            lua_pushstring(L, "incorrect argument");
            lua_error(L);
        }
        sum += lua_tonumber(L, i);
    }
    lua_pushnumber(L, sum/n);        /* first result */
    lua_pushnumber(L, sum);         /* second result */
    return 2;                   /* number of results */
}

所以基本上这个想法是采用 C# 方法,反映其参数和返回值,生成(或从缓存中检索)一个方法,该方法使用上述 Lua API 传递这些参数并返回这些返回类型,最后推送该方法Lua的方法.所以当从 Lua 调用 C# 函数时,它看起来像 lua -> 魔法包装函数 -> 普通的 C# 函数.

So basically the idea is to take a C# method, reflect its parameters and return values, generate (or retrieve from cache) a method that uses the Lua API like above to pass those parameters and return those return types and finally push that method to Lua. So when C# function is called from Lua it looks something like lua -> magic wrapper function -> ordinary C# function.

谢谢.

推荐答案

如果我明白你想要什么,你有两个选择:

If I understand what you want, it seems you have 2 options:

  1. 使用 CodeDOM 在运行时生成和动态编译代码.
  2. 发出实际的 C# 源代码,并在运行时将其动态编译为可调用的程序集.
  1. use the CodeDOM to generate and dynamically compile code, at runtime.
  2. emit actual C# source code, and dynamically compile it into a callable assembly at runtime.

CodeDom 是一种毛茸茸的、非常低级的代码.这个想法是有一个 C# 语言的对象模型.您首先实例化 CodeTypeDeclaration - 这将生成一个类型或类.然后添加属性和字段 - 在这里您可能会为 p/invoke 函数添加 DllImport 声明.然后您对类型使用不同的 CodeDOM 添加方法 - 这将是您插入生成方法的位置.您可以将其设为公开、静态,无论您喜欢什么.

CodeDom is sort of hairy, very low-level code to write. The idea is there's an object model for the C# language. You start by instantiating a CodeTypeDeclaration - this will generate a type or class. Then you add properties and fields - here you would likely add DllImport declarations for your p/invoke functions. Then you use different CodeDOM add methods to the type - this would be where you'd insert the generated method. You could make it public, static, whatever you like.

CodeDOM 看起来像这样:

CodeDOM looks like this:

System.Type mt= a[0].GetType();

System.CodeDom.CodeTypeDeclaration class1 = new System.CodeDom.CodeTypeDeclaration(mt.Name);
class1.IsClass=true;
class1.TypeAttributes = System.Reflection.TypeAttributes.Public;
class1.Comments.Add(new System.CodeDom.CodeCommentStatement("Wrapper class for " + mt.Name));

System.CodeDom.CodeConstructor ctor;
ctor= new System.CodeDom.CodeConstructor();
ctor.Attributes = System.CodeDom.MemberAttributes.Public;
ctor.Comments.Add(new System.CodeDom.CodeCommentStatement("the null constructor"));
class1.Members.Add(ctor);
ctor.Statements.Add(new System.CodeDom.CodeAssignStatement(new System.CodeDom.CodeVariableReferenceExpression("m_wrapped"), new System.CodeDom.CodeObjectCreateExpression(mt)));

ctor= new System.CodeDom.CodeConstructor();
ctor.Attributes = System.CodeDom.MemberAttributes.Public;
ctor.Comments.Add(new System.CodeDom.CodeCommentStatement("the 'copy' constructor"));
class1.Members.Add(ctor);
ctor.Parameters.Add(new System.CodeDom.CodeParameterDeclarationExpression(mt,"X"));
ctor.Statements.Add(new System.CodeDom.CodeAssignStatement(new System.CodeDom.CodeVariableReferenceExpression("m_wrapped"), new System.CodeDom.CodeVariableReferenceExpression("X")));

// embed a local (private) copy of the wrapped type
System.CodeDom.CodeMemberField field1;
field1= new System.CodeDom.CodeMemberField();
field1.Attributes = System.CodeDom.MemberAttributes.Private;
field1.Name= "m_wrapped";
field1.Type=new System.CodeDom.CodeTypeReference(mt);
class1.Members.Add(field1);

...

它继续.和.如您所见,它变得非常丑陋.然后你编译它,我没有展示.我假设你不会想要采用这种方法.

it goes on. and on. As you can see, it gets pretty ugly. Then later you compile it, which I did not show. I'm assuming you're not gonna want to take this approach.

我发现 CodeDom 非常难用;相反,现在当我需要动态生成的程序集时,我将通常通过模板将实际的 C# 代码发送到内存中的字符串中,然后编译那个.就我的目的而言,这要简单得多.编译如下:

I found CodeDom to be pretty crufty to use; instead, now when I need dynamically-generated assemblies, I will emit actual C# code, normally via templates, into a string in memory, and compile that. It's much simpler for my purposes. The compilation looks like this:

var cp = new System.CodeDom.Compiler.CompilerParameters {
  ReferencedAssemblies.Add(filesystemLocation), // like /R: option on csc.exe
  GenerateInMemory = true,    // you will get a System.Reflection.Assembly back
  GenerateExecutable = false, // Dll
  IncludeDebugInformation = false,
  CompilerOptions = ""
};

var csharp = new Microsoft.CSharp.CSharpCodeProvider();

// this actually runs csc.exe:
System.CodeDom.Compiler.CompilerResults cr = 
      csharp.CompileAssemblyFromSource(cp, LiteralSource);


// cr.Output contains the output from the command

if (cr.Errors.Count != 0)
{
    // handle errors
}

System.Reflection.Assembly a = cr.CompiledAssembly;

// party on the type here, either via reflection...
System.Type t = a.GetType("TheDynamicallyGeneratedType");

// or via a wellknown interface

在上面的代码中,LiteralSource 包含要编译的源代码.正如我所说,我通过阅读模板并填写空白来生成它.

In the above code, LiteralSource contains the source code to be compiled. As I said, I generate this by reading a template and filling in the blanks.

这篇关于如何使用代码生成来动态创建 C# 方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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