[英]How to load third-party implementations at run-time
我正在制作一個 .Net 應用程序來管理和安裝 mod。 應用程序本身不應該能夠為任何特定游戲安裝 mod,但應該能夠調用 3rd-party extensions 來這樣做。
假設我的 mod 管理器期望給定接口的實現:
interface IGameManager {
// Deploy a modding configuration to the targeted game
void Deploy();
// Remove all managed mods from the targeted game
void Purge();
// ...
}
而其他人,在不同的代碼庫上工作,實現 IGameManager 來管理特定游戲:
class MinecraftManager: IGameManager {
// ...
}
然后這個人編譯它,發布它,每個人都可以簡單地將此擴展提供給主 mod 管理器,以便它可以管理目標游戲的 mod。
但是如何? 有沒有辦法讓我的應用程序在運行時安全地加載和使用此類第三方實現? 以及如何促進第三方擴展的制作(例如,提供一個可以構建但更優雅和易於維護的界面)?
編輯 1:MinecraftManager 簽名中的無效語法
您實際上是在嘗試設計一個插件系統。 您可以重用許多實現,但總體思路是:
您需要您的經理能夠發現擴展。 有很多方法可以做到這一點,但最簡單和最常用的方法是將擴展程序集放在文件系統中的一個眾所周知的目錄下。 然后您的經理可以通過枚舉文件來枚舉該文件夾中的程序集(或者如果您希望每個擴展都有自己的子文件夾,則枚舉子文件夾)
加載程序集。 為此,您將使用Assembly.Load..
方法之一。 由於無法卸載程序集,您可能希望首先加載該程序集僅用於反射,一旦您確定該程序集有效,您就可以將其加載到ApplicationDomain
中以使用它。
使用 relfection 枚舉您剛剛加載的程序集的所有類,並找到實現正確接口 ( IGameManager
) 的類。 或者,您可以要求擴展包含一個已知名稱的“入口點”類,然后按名稱查找該類(使用反射)。
創建一個類的實例並使用它(也許也將它保存在加載的擴展集合中)
關於擴展必須實現的接口:您應該將接口(和任何其他支持接口)放在單獨的程序集中。 程序集應該只包含接口,不包含實現。 然后您可以發布該程序集。 一旦發布,界面就不應該改變。
如果您需要添加功能,您應該創建一個新界面。 這樣,舊版本的管理器將與較新版本的擴展(也旨在實現新功能)一起使用。 此外,您的經理可以確定擴展實現了哪些接口並采取相應措施(從而保持兼容性)。 如果新功能是強制性的,您的經理應丟棄未實現這兩個接口的任何擴展。
我剛剛發現 dotNet 4 已經有了一個可擴展性框架(此處的文檔),它比使用Assembly.Load()
更干凈、更安全。
這是我用來從給定目錄加載插件的代碼段,如果有人遇到同樣的問題:
// Where T is the type you want to retrieve from the assemblies
private static IEnumerable<Lazy<T>> LoadExternalAssemblyFromPath<T>(string path, string pattern) {
CreateDirectoryIfDoesntExist(path);
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(path, pattern));
CompositionContainer container = new CompositionContainer(catalog);
return container.GetExports<T>();
}
// Usage:
LoadExternalAssemblyFromPath("C:/path/to/plugins", "*.dll");
關於此類插件的實現,Kouvarakis 對此事的解決方案是正確的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.