You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在目标为.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.CSharp
  • Microsoft.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:41:20