C# 动态编译与执行代码实战
在 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# 高级特性及反射机制的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









