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

簡易線上 C# IDE 實作(二):動態編譯執行引擎核心程式碼

     編輯:關於C語言

前言

承接上篇,本文給出 CodeRun 類別完整實作:動態編譯參數設定、原始碼預處理(重導向 Console)、記憶體組件載入、反射呼叫入口點、輸出收集與錯誤處理。

核心程式碼


using System;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace VSOnline.Framework
{
    public class CodeRun
    {
        private string CompilerVersion { get; set; }

        public CodeRun(string compilerVersion) => CompilerVersion = compilerVersion;
        public CodeRun() : this("v3.5") { }

        public List Run(string code, string id, params string[] assemblies)
        {
            Consoler.Outputs[id] = new Consoler();

            var compilerParams = new CompilerParameters
            {
                CompilerOptions = "/target:library /optimize",
                GenerateInMemory = true,
                IncludeDebugInformation = false
            };

            foreach (var asm in assemblies)
                compilerParams.ReferencedAssemblies.Add(asm);

            compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
            compilerParams.ReferencedAssemblies.Add("System.dll");
            if (CompilerVersion == "v3.5")
                compilerParams.ReferencedAssemblies.Add("System.Core.dll");

            string binPath = "";
            try { binPath = HttpContext.Current.Server.MapPath("/bin/"); } catch { }
            compilerParams.ReferencedAssemblies.Add(binPath + "VSOnline.Framework.dll");

            var provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", CompilerVersion } });

            // 重導向 Console.Write/WriteLine 至 Consoler
            code = code.Replace("Console.WriteLine", $"VSOnline.Framework.Consoler.Outputs[\"{id}\"].WriteLine");
            code = code.Replace("Console.Write", $"VSOnline.Framework.Consoler.Outputs[\"{id}\"].Write");

            var results = provider.CompileAssemblyFromSource(compilerParams, code);

            if (results.Errors.HasErrors)
            {
                foreach (CompilerError err in results.Errors)
                    Consoler.Outputs[id].Output.Add(err.ErrorText + "\n");
                return ReturnOutput(id);
            }

            var asm = results.CompiledAssembly;
            var program = asm.CreateInstance("Program");
            var type = program.GetType();
            type.GetMethod("Main").Invoke(program, null);

            return ReturnOutput(id);
        }

        private List ReturnOutput(string id)
        {
            var output = new string[Consoler.Outputs[id].Output.Count];
            Consoler.Outputs[id].Output.CopyTo(output, 0);
            Consoler.Outputs.Remove(id);
            return output.ToList();
        }
    }
}

關鍵技術細節

  1. CompilerParameters:GenerateInMemory=true 避免磁碟 I/O;/optimize 啟用優化。
  2. 參考組件:動態加載 System.Core.dll 支援 LINQ;專案自有 DLL 需部署至 /bin。
  3. 原始碼重寫:字串替換 Console.Write* 為 Consoler 靜態類別呼叫,**簡易但脆弱**(遇字串常量、註解會誤替換),生產環境建議用 Roslyn 語法樹轉換。
  4. 反射呼叫:CreateInstance("Program") → GetMethod("Main").Invoke(),需確保程式碼包含 public class Program { public static void Main() }。
  5. 錯誤處理:編譯錯誤寫入 Consoler 並回傳,不拋出例外中斷流程。

已知限制與改進方向

  • 字串替換風險:改用 Microsoft.CodeAnalysis.CSharp.SyntaxRewriter 精確轉換。
  • 無沙箱:使用者代碼擁有完整 CLR 權限,需配合 AppDomain 沙箱或容器隔離。
  • 單一入口點:僅支援 Program.Main,可擴充自動偵測含 Main 的類別。
  • 記憶體洩漏:動態組件無法卸載(除非卸載整個 AppDomain),長期運行需定期回收 AppDomain。

結語

CodeRun 展示了動態編譯執行的核心流程。下一篇將介紹輸出捕獲機制 Consoler 並給出完整單元測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved