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

動態執行 C# 代碼互動機制(1):從零建構 C# 代碼解釋器

     編輯:關於C語言

動態執行 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 容錯:自動包裝純表達式為變量指派並輸出
  • 反射調用:編譯後透過反射實例化並調用入口方法
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved