游乐游手机版
首页/编程语言/文章详情

c#如何动态编译代码_c#动态编译代码项目实例附完整源码

时间:2026-05-05 09:02
CSharpCodeProvider 在 NET 6+ 中已彻底废弃,必须改用 Microsoft CodeAnalysis(Roslyn)动态编译 如果你仍在尝试使用 CSharpCodeProvider 来实现 C 代码的动态编译,那么请注意:在 NET 6 及更高版本中,此方法已完全失效

CSharpCodeProvider 在 .NET 6+ 中已彻底废弃,必须改用 Microsoft.CodeAnalysis(Roslyn)动态编译

c#如何动态编译代码_c#动态编译代码项目实例附完整源码

如果你仍在尝试使用 CSharpCodeProvider 来实现 C# 代码的动态编译,那么请注意:在 .NET 6 及更高版本中,此方法已完全失效。运行时将直接抛出 Type or namespace name 'CSharpCodeProvider' could not be found 错误。该技术已成为历史,仅适用于 .NET Framework 及早期的 .NET Core 3.1 项目。

为什么 CSharpCodeProvider 编译失败或根本找不到

这是开发者升级项目时首先遇到的障碍。自 .NET 5 起,微软默认移除了对传统 CodeDOM 的支持,而在 .NET 6+ 中,Microsoft.CSharp 命名空间下的编译器类已被彻底删除。即便你在项目文件中添加 true 配置,或手动安装 System.CodeDom NuGet 包,CSharpCodeProvider 的构造函数也会抛出 NotSupportedException 异常。

  • 原生 CSharpCodeProvider 仅在目标框架为 net48netcoreapp3.1 的项目中可用。
  • 目标框架为 net5.0 及以上的项目,必须迁移至 Microsoft.CodeAnalysis(即 Roslyn 编译器)。
  • 若项目目标为 net6.0,代码中仍包含 new CSharpCodeProvider(),编译虽可通过,但运行时必然崩溃。

使用 Roslyn 替代方案:将字符串代码编译至内存程序集

新的解决方案核心是使用 CSharpCompilation 配合 Emit 方法,将输出写入 MemoryStreamAssemblyLoadContext.Load 加载。流程虽比旧方案稍复杂,但优势显著:提供更强的控制力、完全跨平台,并有效规避了旧方案中的反射安全漏洞。

  • 首先,需通过 NuGet 安装包:Microsoft.CodeAnalysis.CSharp(建议使用 v4.0+ 版本)。
  • 必须显式添加所有依赖的程序集引用。例如,System.Console 位于 System.Runtime.dll 中,不能仅模糊引用 "System.dll"
  • 常用快捷方式是使用 MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location) 引用当前程序集。但需注意:若执行程序集为单文件发布(即设置了 PublishTrimmed=true),.Location 属性将返回空字符串。此时需采用备用方案,如使用 typeof(object).Assembly.Location
  • 以下是关键代码示例:
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var references = new List {
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
    // 添加其他必要引用...
};
var compilation = CSharpCompilation.Create(
    assemblyName: $"dyn_{Guid.NewGuid():N}",
    syntaxTrees: new[] { syntaxTree },
    references: references,
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success) {
    var errors = result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
    throw new InvalidOperationException(string.Join("\n", errors.Select(e => e.ToString())));
}
ms.Seek(0, SeekOrigin.Begin);
var assembly = AssemblyLoadContext.Default.LoadFromStream(ms);

动态调用方法时类型名与命名空间的常见错误

旧方案常依赖硬编码的类型名,如 "DynamicClass"。但在 Roslyn 编译后,类型名完全由源代码中的 class 声明决定。若代码定义为 namespace MyNS { public class Worker { ... } },则必须使用 assembly.GetType("MyNS.Worker") 获取类型,遗漏命名空间将返回 null

  • 实用建议:在构造源码字符串时,强制指定命名空间。例如:@"using System; namespace Dyn { public class Entry { public static void Run() { Console.WriteLine(""ok""); } } }"
  • 调用静态方法使用 type.GetMethod("Run").Invoke(null, null);调用实例方法需先通过 Activator.CreateInstance(type) 创建对象。
  • 若源代码包含泛型或异步方法,使用 GetMethod 时必须传入精确的方法签名。例如:GetMethod("RunAsync", new[] { typeof(CancellationToken) })

安全与性能的关键约束

动态编译技术若用于生产环境,必须设定明确的边界以确保稳定与安全:

  • 超时控制:Roslyn 编译过程本身不支持 CancellationToken。常见做法是用 Task.Run(...).Wait(timeout) 包裹编译任务,超时则取消并释放相关的 AssemblyLoadContext
  • 内存泄漏:每次调用 LoadFromStream 都可能注册新的上下文。若不调用 AssemblyLoadContext.Unload,程序集将永久驻留内存。由于 .NET 6+ 的默认上下文不可卸载,建议自定义派生类并设置 IsCollectible = true
  • 源码安全校验:严禁将未经处理的用户输入直接传入 ParseText。即使存在沙箱环境,unsafestackalloc 或 P/Invoke 等代码仍可能突破限制。生产环境中,务必对源码的抽象语法树(AST)节点进行白名单校验。

调试环节同样需注意:编译错误信息存储在 Diagnostics 集合中,其行号对应原始字符串位置,而非文件行号。出错时,需自行按 \n 拆分源码字符串并标注行号——此细节常被忽略,却极易在部署初期引发问题。

来源:https://www.php.cn/faq/2333138.html
上一篇Debian系统C++编译器怎么配 下一篇C++在Debian上的安全性如何保障
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
IDEA中SpringBoot项目热部署实现指南
编程语言 · 2026-07-11

IDEA中SpringBoot项目热部署实现指南

Springboot项目在IDEA中实现热部署需依次完成:开启自动编译(静态与动态编译)、启用热部署策略、引入spring-boot-devtools依赖、关闭浏览器缓存,最后启动测试即可生效,省去手动重启时间,大幅提升开发效率。

Java实现Excel转JSON的代码详解
编程语言 · 2026-07-11

Java实现Excel转JSON的代码详解

使用Java结合FreeSpire XLS库可自动将Excel转为JSON,通过读取工作表并映射为键值对,高效支持数据迁移、报表导出与系统对接,大幅提升效率并消除手动录入错误。

Golang高效布谷鸟过滤器多字符集字符串过滤
编程语言 · 2026-07-11

Golang高效布谷鸟过滤器多字符集字符串过滤

布谷鸟过滤器支持删除操作,通过指纹截断与双哈希定位实现多字符集高效过滤。指纹截断需采用fnv64a或xxhash快速哈希并取低8位,桶索引使用独立双哈希避免假阳性。并发安全需以固定数组配合sync atomic实现。

Java使用Poi-tl按Word模板生成动态表格
编程语言 · 2026-07-11

Java使用Poi-tl按Word模板生成动态表格

Poi-tl是Word导出工具,文档周全,支持多级合并表头生成。通过{{text}}、{{?var}}、{{ table}}标签处理模板,传入TableRenderData对象即可动态渲染表格。示例展示用户信息表格生成,并提供工具类消除表格首行缩进,确保格式正确。

Go语言微服务集成Swagger生成交互式API文档完整教程
编程语言 · 2026-07-11

Go语言微服务集成Swagger生成交互式API文档完整教程

在Go微服务中集成Swagger常见四大问题:swaginit初始化失败需确保存在packagemain及注释;SwaggerUI加载失败因路由映射错误或未导入docs;@Param注解必须严格遵循格式;部署后接口文档显示localhost因硬编码host,应删除或通过环境变量动态注入。