AppDomain 角色與必要性
動態編譯生成的組件無法在預設 AppDomain 卸載,導致記憶體洩漏。解決:建立子 AppDomain 載入動態組件,使用完畢 `AppDomain.Unload()` 徹底釋放。
AppDomain 運作模型
- 類似獨立進程,但運行於同一進程內,資源開銷較輕。
- 跨 AppDomain 物件傳遞預設為 **Remoting 代理**(`MarshalByRefObject`),非序列化複寫。
- 直接持有跨域物件引用會阻礙目標 AppDomain 卸載(GC 無法回收代理鏈)。
關鍵實作規則
- **所有跨域暴露類別必須繼承 `MarshalByRefObject`**;僅標記 `[Serializable]` 會導致序列化複寫,主域保留引用,卸載失敗。
- 動態編譯的程式碼類別需繼承 `MarshalByRefObject` 並實作介面(如 `IHelloWorld`)。
- 主域透過介面引用操作子域物件,用完呼叫 `AppDomain.Unload(childDomain)`。
封裝類別 `CSharpProvider` 缺陷與改進
- 缺乏多檔案編譯介面(單一腳本場景可接受)。
- 缺乏列舉動態建立物件的介面(後續擴展)。
- 調試支援僅限檔案編譯(需 `.pdb`),記憶體編譯無法指定原始碼映射(待解決)。
原始碼片段
// 動態程式碼範例
using MyInterface;
[Serializable]
public class HelloWorld : MarshalByRefObject, IHelloWorld
{
public string Say() => "Hi";
}
配套原始碼下載見原文連結。