動態執行 C# 代碼的價值
- 互動式開發:輸入代碼即時編譯執行,如 REPL 環境
- 熱重載邏輯:外部腳本修改無需重啟主程式,下次執行自動生效
- 規則引擎基礎:業務規則外部化,支援運行時動態調整
核心類別:Context 解釋器環境
public class Context
{
public CSharpCodeProvider CodeProvider { get; set; }
public IDictionary Assemblies { get; set; }
public Context()
{
CodeProvider = new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v3.5" } });
Assemblies = new Dictionary();
// 載入當前應用程式域所有組件
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
AddAssembly(a);
// 監聽新組件載入
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
}
private void AddAssembly(Assembly a)
{
if (a != null) Assemblies.Add(a.FullName, a);
}
void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Assembly a = args.LoadedAssembly;
if (!Assemblies.ContainsKey(a.FullName)) AddAssembly(a);
}
public CompilerParameters CreateCompilerParameters()
{
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
foreach (Assembly a in Assemblies.Values)
cp.ReferencedAssemblies.Add(a.Location);
return cp;
}
public void Invoke(String cmd)
{
String inputCmdString = cmd.Trim();
if (String.IsNullOrEmpty(inputCmdString)) return;
String fullCmd = BuildFullCmd(inputCmdString);
CompilerResults cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);
// 錯誤處理與 CS0201 自動修正(表達式語句需包裝)
if (cr.Errors.HasErrors)
{
Boolean recompileSwitch = true;
foreach (CompilerError err in cr.Errors)
if (!err.ErrorNumber.Equals("CS0201")) { recompileSwitch = false; break; }
if (recompileSwitch)
{
String dynaName = "TempArg_Dynamic_" + DateTime.Now.Ticks;
inputCmdString = String.Format(" var {0} = ", dynaName) + inputCmdString;
inputCmdString += ";\n System.Console.WriteLine(" + dynaName + ");";
fullCmd = BuildFullCmd(inputCmdString);
cr = CodeProvider.CompileAssemblyFromSource(CreateCompilerParameters(), fullCmd);
}
if (cr.Errors.HasErrors)
{
Console.WriteLine("編譯錯誤:");
foreach (CompilerError err in cr.Errors)
{
Console.WriteLine(err.ErrorNumber);
Console.WriteLine(err.ErrorText);
}
return;
}
}
Assembly assem = cr.CompiledAssembly;
Object dynamicObject = assem.CreateInstance("Test.DynamicClass");
Type t = assem.GetType("Test.DynamicClass");
MethodInfo minfo = t.GetMethod("MethodInstance");
minfo.Invoke(dynamicObject, null);
}
private String BuildFullCmd(String inputCmdString)
{
return @"
namespace Test
{
public class DynamicClass
{
public void MethodInstance()
{
" + inputCmdString + @";
}
}
}";
}
}
主程式進入點
static void Main(string[] args)
{
Console.Write(">> ");
String cmd;
Context cxt = new Context();
while ((cmd = Console.ReadLine().Trim()) != "exit")
{
if (!String.IsNullOrEmpty(cmd))
{
Console.WriteLine();
cxt.Invoke(cmd);
}
Console.Write("\n>> ");
}
}
關鍵技術點
- In-Memory 編譯:`GenerateInMemory = true` 避免磁碟 I/O
- 組件自動發現:監聽 `AssemblyLoad` 事件動態擴展參考
- CS0201 容錯:自動包裝純表達式為變量指派並輸出
- 反射調用:編譯後透過反射實例化並調用入口方法