核心問題
- 如何將網頁文字方塊的程式碼編譯執行?
- 如何捕獲
Console.Write/WriteLine 輸出並回傳網頁?
解決方案架構
Consoler - 自訂輸出收集器
public class Consoler
{
public static Dictionary Outputs { get; set; } = new Dictionary();
public List Output { get; private set; } = new List();
public void Write(object s) => Output.Add(s.ToString());
public void WriteLine(object s) => Output.Add(s.ToString() + "\n");
}
CodeRun - 動態編譯執行引擎
public class CodeRun
{
public string CompilerVersion { get; set; } = "v3.5";
public List Run(string code, string id, params string[] assemblies)
{
Consoler.Outputs[id] = new Consoler();
var cp = new CompilerParameters
{
CompilerOptions = "/target:library /optimize",
GenerateInMemory = true,
IncludeDebugInformation = false
};
foreach (var asm in assemblies) cp.ReferencedAssemblies.Add(asm);
cp.ReferencedAssemblies.AddRange(new[] { "mscorlib.dll", "System.dll", "System.Core.dll", "VSOnline.Framework.dll" });
var provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", CompilerVersion } });
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(cp, 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");
program.GetType().GetMethod("Main").Invoke(program, null);
return ReturnOutput(id);
}
List ReturnOutput(string id)
{
var arr = new string[Consoler.Outputs[id].Output.Count];
Consoler.Outputs[id].Output.CopyTo(arr, 0);
Consoler.Outputs.Remove(id);
return new List(arr);
}
}
關鍵技巧
- 字串取代將
Console 導向 Consoler,避免重導向標準輸出流的複雜性。
CompilerParameters.GenerateInMemory = true 直接在記憶體產生組件,無臨時檔案。
- 每次執行使用唯一
id 隔離輸出緩衝區。