c# - 如何使用代码生成动态创建C#方法?

标签 c# .net reflection code-generation lua

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

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

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# 方法,反射(reflect)它的参数和返回值,生成(或从缓存中检索)一个方法,该方法使用上面的 Lua API 来传递这些参数并返回那些返回类型,最后推送它Lua 的方法。因此,当从 Lua 调用 C# 函数时,它看起来像 lua -> 魔术包装函数 -> 普通 C# 函数。

谢谢。

最佳答案

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

  1. use the CodeDOM在运行时生成和动态编译代码。
  2. 发出实际的 C# 源代码,并在运行时将其动态编译成可调用的程序集。

CodeDom 有点复杂,编写的代码非常低级。这个想法是有一个 C# 语言的对象模型。您首先实例化一个 CodeTypeDeclaration - 这将生成一个类型或类。然后添加属性和字段 - 在这里您可能会为 p/invoke 函数添加 DllImport 声明。然后您使用不同的 CodeDOM 添加方法到该类型 - 这将是您插入生成的方法的地方。你可以把它公开,静态,任何你喜欢的。

CodeDOM 看起来像这样:

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);

...

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


我发现 CodeDom 使用起来相当笨拙;相反,现在当我需要动态生成的程序集时,我将发出实际的 C# 代码,通常是通过模板,将其放入内存中的字符串中,然后编译那个。就我的目的而言,它要简单得多。编译看起来像这样:

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 包含了要编译的源代码。正如我所说,我通过阅读模板并填写空白来生成它。

关于c# - 如何使用代码生成动态创建C#方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2354587/

相关文章:

c# - 克隆一个包含许多对象并列出的对象 C#

c# - 其他控件内的中继器

c# - 缓存农场,读取池

.net - 检查内存转储中所有实例的字段

go - panic : reflect: call of reflect. Value.Call 零值

grails - 如何获取从 Groovy 特征继承的属性的 java.reflect.Field 值?

c# - “Safe handle has been closed” 线程中止 : can program crash be avoided?

c# - Entity Framework 6.0 代码优先迁移 - 模型/数据库兼容性错误?

c# - Foreach 循环不前进也不抛出异常

c# - 具有高级功能的表