在目标为.NET Framework 4.6的应用中部署Roslyn Compiler API的技术问询
我刚好在.NET Framework 4.6环境下做过从CSharpCodeProvider迁移到Roslyn Compiler API的工作,针对你的需求(代码分析+程序化构建程序集),给你整理了具体的实现方案和示例代码:
1. 先安装兼容的Roslyn NuGet包
因为你的目标框架是.NET Framework 4.6,要选择对该版本兼容的Roslyn包。推荐安装以下两个包(版本选2.9.0,这个版本对.NET Framework 4.6支持最稳定,更高版本的Roslyn可能需要4.6.1及以上):
Microsoft.CodeAnalysis.CSharpMicrosoft.CodeAnalysis.Common
你可以通过NuGet包管理器或者Package Manager Console安装:
Install-Package Microsoft.CodeAnalysis.CSharp -Version 2.9.0 Install-Package Microsoft.CodeAnalysis.Common -Version 2.9.0
2. 代码分析:语法与语义检查
Roslyn的优势之一就是强大的代码分析能力,你可以轻松解析用户输入的代码片段,做语法检查、语义验证,甚至自定义分析规则。
示例:语法分析+错误检查
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Collections.Generic; using System.Linq; public static List<string> AnalyzeUserCode(string userCode) { // 解析代码为语法树 var syntaxTree = CSharpSyntaxTree.ParseText(userCode); // 获取语法错误 var syntaxErrors = syntaxTree.GetDiagnostics() .Where(d => d.Severity == DiagnosticSeverity.Error) .Select(d => $"语法错误:{d.GetMessage()} 位置:{d.Location}") .ToList(); if (syntaxErrors.Any()) return syntaxErrors; // 语义分析(需要创建编译上下文) var references = new List<MetadataReference> { // 引用基础程序集,比如System.dll MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; var compilation = CSharpCompilation.Create("TempCompilation") .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddReferences(references) .AddSyntaxTrees(syntaxTree); // 获取语义错误 var semanticErrors = compilation.GetDiagnostics() .Where(d => d.Severity == DiagnosticSeverity.Error) .Select(d => $"语义错误:{d.GetMessage()}") .ToList(); return semanticErrors; }
这个方法会先检查用户代码的语法问题,再通过创建临时编译上下文做语义验证,返回所有错误信息,方便你提前向用户反馈问题。
3. 程序化构建并编译程序集
这部分是替换CSharpCodeProvider的核心逻辑,Roslyn通过CSharpCompilation类来构建编译流程,最终生成程序集。
示例:编译用户代码为内存程序集
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; using System.IO; using System.Reflection; public static Assembly CompileUserCodeToAssembly(string userCode, string assemblyName = "UserScriptAssembly") { // 1. 准备需要引用的程序集 var references = new List<MetadataReference> { // 引用当前应用依赖的基础程序集 MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), MetadataReference.CreateFromFile(typeof(IEnumerable<>).Assembly.Location), // 如果用户代码需要其他引用,可以在这里添加,比如你的应用自身的程序集 // MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location) }; // 2. 创建编译对象 var compilation = CSharpCompilation.Create(assemblyName) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) // 这里可以设置编译选项,比如优化等级、是否允许不安全代码等 .WithOptimizationLevel(OptimizationLevel.Debug) .AllowUnsafe(false)) .AddReferences(references) .AddSyntaxTrees(CSharpSyntaxTree.ParseText(userCode)); // 3. 执行编译,输出到内存流 using var ms = new MemoryStream(); var emitResult = compilation.Emit(ms); // 4. 处理编译结果 if (!emitResult.Success) { var errors = string.Join(Environment.NewLine, emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error) .Select(d => $"{d.Id}: {d.GetMessage()}")); throw new InvalidOperationException($"编译失败:{errors}"); } // 5. 加载内存中的程序集 ms.Seek(0, SeekOrigin.Begin); return Assembly.Load(ms.ToArray()); }
调用编译后的程序集
编译完成后,你可以像之前使用CSharpCodeProvider生成的程序集一样,通过反射加载并执行其中的类型或方法:
var userAssembly = CompileUserCodeToAssembly(@" public class UserScript { public string Execute() { return ""Hello from Roslyn compiled code!""; } }"); var scriptType = userAssembly.GetType("UserScript"); var instance = Activator.CreateInstance(scriptType); var result = scriptType.GetMethod("Execute").Invoke(instance, null); Console.WriteLine(result); // 输出:Hello from Roslyn compiled code!
关键注意事项
- 程序集引用:确保你添加了用户代码需要的所有程序集引用,否则会出现“找不到类型”的语义错误。你可以通过
Assembly.GetReferencedAssemblies()遍历当前应用的引用,自动添加到Roslyn的编译引用中。 - 安全风险:用户输入的代码会被编译执行,存在安全隐患(比如恶意代码删除文件、访问敏感资源)。如果是公开应用,建议使用沙箱技术(比如
AppDomain隔离、.NET Code Access Security等)限制代码权限。 - 性能优化:如果相同的代码片段会被多次编译,可以缓存编译后的程序集或者语法树,避免重复编译带来的性能损耗。
内容的提问来源于stack exchange,提问作者user9564952
相关产品推荐
相关产品推荐

