C# 动态编译与执行代码实战

阿橙
阿橙
发布时间:2026-10-15 | 4人浏览过
原创

在 C# 开发中,有时需要在运行时动态生成并执行代码逻辑,例如构建热插拔插件系统或简易脚本引擎。本文将演示如何利用 .NET 框架提供的 CodeDomProvider 类,将字符串形式的源代码编译为内存中的程序集(Assembly),并通过反射机制安全地执行其中的方法。

动态编译的核心原理

C# 的动态编译主要依赖 System.CodeDom 命名空间下的提供器。流程包括:准备源码字符串、配置编译参数(如引用程序集、是否生成文件)、调用编译器执行编译,最后从编译结果中提取程序集并进行反射调用。

具体操作步骤

第1步:定义要执行的动态代码

首先,需要构建一个包含完整 C# 类的字符串。该示例构建了一个名为 HelloWorld.HelloWorld 的类,其中包含一个静态方法 SayHello。

// 动态构建的代码
static string HelloWorld()
{
    StringBuilder sbCode = new StringBuilder();
    sbCode.AppendLine("using System;");
    sbCode.AppendLine("namespace HelloWorld");
    sbCode.AppendLine("{");
    sbCode.AppendLine(" class HelloWorld");
    sbCode.AppendLine(" {");
    sbCode.AppendLine(" public static void SayHello()");
    sbCode.AppendLine(" {");
    sbCode.AppendLine(" Console.WriteLine(\"Hello~ World~!\");");
    sbCode.AppendLine(" }");
    sbCode.AppendLine(" }");
    sbCode.AppendLine("}");
    return sbCode.ToString();
}

第2步:配置编译器参数

使用 CompilerParameters 对象设置编译行为。关键设置如下:

  • ReferencedAssemblies:添加编译所需的外部程序集引用,如 System.dll。
  • GenerateExecutable:设为 false,因为我们需要的是类库/程序集而非独立可执行文件。
  • GenerateInMemory:设为 true,将编译后的程序集直接加载到内存,无需写入磁盘文件。
// 编译器参数
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;

第3步:执行动态编译

通过 CodeDomProvider.CreateProvider("C#") 获取 C# 编译器实例,并调用 CompileAssemblyFromSource 方法传入源码和参数,获得 CompilerResults。

// 编译器
CodeDomProvider cdp = CodeDomProvider.CreateProvider("C#");

// 编译结果
CompilerResults cr = cdp.CompileAssemblyFromSource(cp, HelloWorld());

第4步:检查结果并获取程序集

编译完成后,首先检查 cr.Errors.HasErrors 判断是否有语法错误。若无错误,可通过 cr.CompiledAssembly 获取编译后的 Assembly 对象。

if (cr.Errors.HasErrors)
    Console.WriteLine("编译出错!");
else
{
    // 编译后的程序集
    Assembly ass = cr.CompiledAssembly;
    // ... 后续执行逻辑
}

第5步:通过反射调用方法

在获取程序集后,使用 GetType 定位到完整的类名(注意命名空间和类名的全限定名:HelloWorld.HelloWorld)。接着获取目标方法 SayHello 的 MethodInfo,最后使用 Invoke 执行该方法。由于 SayHello 是静态方法,第一个参数传 null。

// 得到HelloWorld类中的SayHello方法
Type type = ass.GetType("HelloWorld.HelloWorld");
MethodInfo mi = type.GetMethod("SayHello");

// 执行
mi.Invoke(null, null); // 对于静态方法,instance为null

第6步:整合为完整入口点

将上述步骤组合在 Main 方法中,形成完整的动态编译执行流程。

static void Main(string[] args)
{
    // 1. 获取编译器
    CodeDomProvider cdp = CodeDomProvider.CreateProvider("C#");
    
    // 2. 配置参数
    CompilerParameters cp = new CompilerParameters();
    cp.ReferencedAssemblies.Add("System.dll");
    cp.GenerateExecutable = false;
    cp.GenerateInMemory = true;
    
    // 3. 编译源码
    CompilerResults cr = cdp.CompileAssemblyFromSource(cp, HelloWorld());
    
    // 4. 处理结果
    if (cr.Errors.HasErrors)
    {
        Console.WriteLine("编译出错!");
        foreach (var error in cr.Errors)
        {
            Console.WriteLine(error.ErrorText);
        }
    }
    else
    {
        // 5. 获取程序集
        Assembly ass = cr.CompiledAssembly;
        
        // 6. 反射调用
        Type type = ass.GetType("HelloWorld.HelloWorld");
        MethodInfo mi = type.GetMethod("SayHello");
        mi.Invoke(null, null);
    }
}

完成判断与安全建议

当控制台输出 Hello~ World~! 时,表示动态编译与执行成功。在实际应用中,动态代码可能存在安全风险,建议对输入源码进行严格校验,或在受限的沙箱环境中运行。

以上就是 C# 使用 CodeDomProvider 动态编译和执行代码的详细内容,更多关于 C# 高级特性及反射机制的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表