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

.NET 動態編譯技術全解析:從基本編譯到跨應用程式域隔離執行

     編輯:關於.NET

前言

動態編譯並執行程式碼是 .NET 提供的強大擴充手段,適用於模板生成、外掛邏輯擴充等場景。核心命名空間為 System.CodeDom.Compiler 與 Microsoft.CSharp(或 Microsoft.VisualBasic),搭配反射實現動態呼叫。

基本動態編譯流程

  1. 將原始碼讀入字串
  2. 建立 CSharpCodeProvider 實例
  3. 設定 CompilerParameters(參考組件、GenerateInMemory = true)
  4. 呼叫 CompileAssemblyFromSource 編譯
  5. 若無錯誤,透過 Assembly.CreateInstance 建立實例
  6. 以反射 InvokeMember 呼叫方法
string strSourceCode = this.txtSource.Text;
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
parameters.GenerateInMemory = true;

CompilerResults results = provider.CompileAssemblyFromSource(parameters, strSourceCode);
if (results.Errors.HasErrors) { /* 處理錯誤 */ return; }

Assembly asm = results.CompiledAssembly;
object obj = asm.CreateInstance("Dynamicly.HelloWorld");
object[] args = new object[] { "Allan." };
string result = (string)obj.GetType().InvokeMember("GetTime", BindingFlags.InvokeMethod, null, obj, args);
this.txtResult.Text = result;

測試程式碼:

using System;
namespace Dynamicly
{
    public class HelloWorld
    {
        public string GetTime(string strName)
        {
            return "Welcome " + strName + ", Check in at " + DateTime.Now.ToString();
        }
    }
}

進階:跨 AppDomain 隔離與卸載

基本作法會將組件載入當前 AppDomain,無法卸載,長期執行會導致記憶體洩漏。解決方案:

  1. 建立獨立 AppDomain
  2. 編譯輸出至磁碟(GenerateInMemory = false,指定 OutputAssembly)
  3. 定義跨 AppDomain 介面 IRemoteInterface 及工廠 RemoteLoaderFactory : MarshalByRefObject
  4. 在新 AppDomain 建立工廠實例,透過工廠載入組件並取得介面
  5. 呼叫介面方法完成跨域調用
  6. 使用完畢後 AppDomain.Unload 卸載域並刪除 DLL
// 介面與工廠(獨立組件 RemoteAccess.dll)
public interface IRemoteInterface { object Invoke(string method, object[] parameters); }
public class RemoteLoaderFactory : MarshalByRefObject
{
    public IRemoteInterface Create(string assemblyFile, string typeName, object[] constructArgs)
    {
        return (IRemoteInterface)Activator.CreateInstanceFrom(assemblyFile, typeName, false,
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance,
            null, constructArgs, null, null, null).Unwrap();
    }
}
// 主程式調用
AppDomainSetup setup = new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
AppDomain domain = AppDomain.CreateDomain("MyAppDomain", null, setup);

CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("RemoteAccess.dll");
parameters.GenerateInMemory = false;
parameters.OutputAssembly = "DynamicalCode.dll";

CompilerResults results = provider.CompileAssemblyFromSource(parameters, strSourceCode);
// ... 錯誤處理 ...

RemoteLoaderFactory factory = (RemoteLoaderFactory)domain.CreateInstanceAndUnwrap("RemoteAccess", "RemoteAccess.RemoteLoaderFactory");
IRemoteInterface remote = factory.Create("DynamicalCode.dll", "Dynamicly.HelloWorld", null);
string result = (string)remote.Invoke("GetTime", new object[] { "Allan." });

remote = null;
AppDomain.Unload(domain);
File.Delete("DynamicalCode.dll");

客戶端類別需繼承 MarshalByRefObject 並實作 IRemoteInterface:

public class HelloWorld : MarshalByRefObject, IRemoteInterface
{
    public object Invoke(string method, object[] parameters)
    {
        return this.GetType().InvokeMember(method, BindingFlags.InvokeMethod, null, this, parameters);
    }
    public string GetTime(string strName) => "Welcome " + strName + ", Check in at " + DateTime.Now;
}

結語

透過獨立 AppDomain 載入動態組件,可實現組件卸載、記憶體控制與安全性隔離,是生產環境動態編譯的最佳實踐。

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