承接上篇,本文給出 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();
}
}
}
GenerateInMemory=true 避免磁碟 I/O;/optimize 啟用優化。System.Core.dll 支援 LINQ;專案自有 DLL 需部署至 /bin。Console.Write* 為 Consoler 靜態類別呼叫,**簡易但脆弱**(遇字串常量、註解會誤替換),生產環境建議用 Roslyn 語法樹轉換。CreateInstance("Program") → GetMethod("Main").Invoke(),需確保程式碼包含 public class Program { public static void Main() }。Consoler 並回傳,不拋出例外中斷流程。Microsoft.CodeAnalysis.CSharp.SyntaxRewriter 精確轉換。AppDomain 沙箱或容器隔離。Program.Main,可擴充自動偵測含 Main 的類別。CodeRun 展示了動態編譯執行的核心流程。下一篇將介紹輸出捕獲機制 Consoler 並給出完整單元測試。