首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

打造簡易線上 C# IDE:動態編譯與輸出捕獲核心技術解析

     編輯:關於C#

核心架構

  1. 前端:文字編輯器 (CodeMirror/Monaco) + 執行按鈕。
  2. 後端:
    a. 接收程式碼字串
    b. 字串置換:`Console.Write/WriteLine` → 自訂輸出收集器
    c. `CSharpCodeProvider` / `Roslyn` 動態編譯為記憶體組件
    d. 反射呼叫 `Program.Main()`
    e. 回傳收集的輸出/編譯錯誤

關鍵元件:輸出捕獲器


public static class Consoler
{
    // 隔離不同請求的輸出
    public static ConcurrentDictionary Outputs { get; } = new();

    public List Output { get; } = new List();

    public void Write(object o) => Output.Add(o?.ToString() ?? "");
    public void WriteLine(object o) => Output.Add((o?.ToString() ?? "") + "\n");

    public static Consoler GetOrCreate(string sessionId) => 
        Outputs.GetOrAdd(sessionId, _ => new Consoler());

    public static List TakeOutput(string sessionId)
    {
        if (Outputs.TryRemove(sessionId, out var c))
            return c.Output;
        return new List();
    }
}

動態編譯與執行核心 (CSharpCodeProvider - Legacy)


public class CodeRunner
{
    public List Run(string code, string sessionId, params string[] extraAssemblies)
    {
        var consoler = Consoler.GetOrCreate(sessionId);
        
        // 1. 置換 Console 輸出
        code = code.Replace("Console.WriteLine", $"Consoler.GetOrCreate(\"{sessionId}\").WriteLine");
        code = code.Replace("Console.Write", $"Consoler.GetOrCreate(\"{sessionId}\").Write");

        // 2. 編譯參數
        var cp = new CompilerParameters
        {
            GenerateInMemory = true,
            TreatWarningsAsErrors = false,
            CompilerOptions = "/target:exe /optimize",
            ReferencedAssemblies = { "mscorlib.dll", "System.dll", "System.Core.dll" }
        };
        foreach (var asm in extraAssemblies) cp.ReferencedAssemblies.Add(asm);

        // 3. 編譯
        var provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } });
        var results = provider.CompileAssemblyFromSource(cp, code);

        // 4. 處理編譯錯誤
        if (results.Errors.HasErrors)
        {
            foreach (CompilerError err in results.Errors)
                consoler.WriteLine($"錯誤 {err.Line}: {err.ErrorText}");
            return Consoler.TakeOutput(sessionId);
        }

        // 5. 執行
        try
        {
            var asm = results.CompiledAssembly;
            var entry = asm.EntryPoint; // 尋找 Main
            if (entry != null)
                entry.Invoke(null, new object[] { Array.Empty() }); // 呼叫 Main()
        }
        catch (Exception ex)
        {
            consoler.WriteLine($"執行錯誤: {ex.Message}");
        }

        return Consoler.TakeOutput(sessionId);
    }
}

現代化升級:Roslyn (Microsoft.CodeAnalysis.CSharp.Scripting)


// NuGet: Microsoft.CodeAnalysis.CSharp.Scripting
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

public class RoslynRunner
{
    public async Task RunAsync(string code)
    {
        var options = ScriptOptions.Default
            .WithReferences(typeof(object).Assembly, typeof(Console).Assembly, typeof(Enumerable).Assembly)
            .WithImports("System", "System.Linq", "System.Collections.Generic", "System.Text");

        // 建立自訂 Console 重導
        var output = new StringBuilder();
        var customConsole = new CustomConsole(output);

        var globals = new { Console = customConsole };
        var script = CSharpScript.Create(code, options, globalsType: typeof(Globals));
        var result = await script.RunAsync(globals);
        
        return output.ToString();
    }

    private class CustomConsole
    {
        private readonly StringBuilder _sb;
        public CustomConsole(StringBuilder sb) => _sb = sb;
        public void Write(object o) => _sb.Append(o);
        public void WriteLine(object o) => _sb.AppendLine(o?.ToString());
    }
    private class Globals { public CustomConsole Console; }
}

Roslyn 優勢:**編譯快、支援 C# 新語法、Scripting API 直覺、可逐行互動 (REPL)**。

安全性強化 (必要)

總結

線上 C# IDE 核心在於 **`Console` 重導向捕獲輸出** + **動態編譯執行**。Legacy 方案用 `CSharpCodeProvider` 相容 .NET Framework;現代專案請採用 **Roslyn Scripting API**,獲得更佳效能、語法支援與安全性擴充性。務必實作**沙箱隔離、資源配額、逾時控制**,才能安全對外開放。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
風險緩解措施
惡意程式碼 (檔案/網路/進程)沙箱:AppDomain 隔離 (Legacy) / 容器化 / gVisor / WASM
無限迴圈/記憶體耗盡執行逾時 (`CancellationToken`)、記憶體限制 (Job Object / cgroups)
敏感資訊洩露移除敏感組件參考、過濾 `using`、審計日誌
編譯資源耗盡限制並發編譯數、快取編譯結果