.net 6+ 动态编译必须用 roslyn,csharpcodeprovider 已被源码级移除;核心步骤为解析语法树、显式添加 metadatareference、创建编译并指定 dynamicallylinkedlibrary、用 assemblyloadcontext 加载;单文件发布需谨慎处理程序集路径。

C# 动态编译在 .NET 6+ 中只有一条路可走:必须用 Roslyn,CSharpCodeProvider 已彻底失效,运行时直接抛 NotSupportedException 或编译器找不到类型。
为什么 new CSharpCodeProvider() 在 net6.0+ 项目里会炸
不是配置没加、不是 NuGet 没装,是微软从源码层删了它。.NET 5 起默认禁用 CodeDOM,.NET 6+ 完全移除 Microsoft.CSharp.CSharpCodeProvider 类型——哪怕你手动引用 System.CodeDom 包,构造函数也会 throw NotSupportedException。
常见错误现象:
- 编译不报错,但运行时提示
Type or namespace name 'CSharpCodeProvider' could not be found - 或更隐蔽的:
NotSupportedException: CodeDOM provider type 'Microsoft.CSharp.CSharpCodeProvider' is not supported - 目标框架是
net6.0却还在写using Microsoft.CSharp;+new CSharpCodeProvider(),等于主动踩坑
用 Roslyn 编译字符串代码的核心四步
本质是:解析 → 绑定 → 生成 IL → 加载执行。每一步都得显式控制,没有“自动引用”这回事。
-
CSharpSyntaxTree.ParseText(code):把字符串转成语法树,注意编码(建议传Encoding.UTF8) - 所有依赖必须显式添加为
MetadataReference,比如Console.WriteLine需要typeof(Console).Assembly.Location,不能只写"System.dll" -
CSharpCompilation.Create()时,OutputKind推荐用DynamicallyLinkedLibrary(DLL),别用ConsoleApplication(入口点处理复杂) - Emit 到
MemoryStream后,必须用AssemblyLoadContext.Load(stream)加载,不能直接Assembly.Load(byte[])(后者在单文件发布下可能失败)
单文件发布(PublishTrimmed=true)下 MetadataReference 的坑
调用 Assembly.GetExecutingAssembly().Location 会返回空字符串,因为单文件发布把所有 DLL 打包进一个 exe,没独立路径。
安全 fallback 写法:
string location = typeof(object).Assembly.Location;
if (string.IsNullOrEmpty(location))
{
location = typeof(Console).Assembly.Location; // 或 typeof(string).Assembly.Location
}
var reference = MetadataReference.CreateFromFile(location);
更稳妥的做法是:对关键基础类型(object、string、Console、IEnumerable)逐个尝试 .Assembly.Location,取第一个非空值。
动态编译后执行方法的典型模式
Roslyn 编译出来的是普通程序集,反射调用逻辑和以前一样,但要注意命名空间和类名必须完全匹配源码字符串。
- 若源码含
namespace MyNS { public class Worker { public void Run() {} } },则反射要用assembly.GetType("MyNS.Worker") - 静态方法直接
type.GetMethod("Run").Invoke(null, null);实例方法需先Activator.CreateInstance(type) - 别忘了捕获
TargetInvocationException——实际业务异常会被包一层,InnerException才是真实错误
最易被忽略的点:编译成功不等于能跑通。引用漏一个(比如忘了加 System.Runtime.dll),Emit() 可能静默失败或抛出模糊的 InvalidOperationException;而加载后第一次调用方法时才暴露 TypeLoadException。务必在 Emit() 后检查返回值 success == true,并遍历 diagnostics 输出所有警告和错误。










