面臨的問題
在開發插件系統中,我們通常會面臨這樣的問題:
一些功能并不是在開啟時就要被使用的,例如vs中的大量功能對一個大部分程式員來說用不着,但架構本身卻應該向使用者提供該插件的相應資訊?
在可視化的插件功能清單中,我們不僅希望提供簡單的插件名稱資訊,更希望能以圖檔,或動畫等形式展示其功能特性,便于使用者選擇。
插入輔助類來解決上一個問題? 想法雖好,但破壞了“插件”的精髓,它應該是獨立可插拔的,如果存在其之外的輔助類,那真是得不償失。
據我所知,.net的mef插件系統提供了完整的插件系統架構,但可定制化程度不高。 一些插件功能是不需要每次都調用的,如果執行個體化所有的插件會帶來很大的資源開銷,而且不友善管理。是以本文将通過一些技巧,實作本文标題的目标:不執行個體化擷取插件資訊和可視化方法。我的項目本身是基于wpf的,但基本不影響整個文章的通用性。
2. 改造attribute,提供類資訊的方法
通過繼承atrribute,擴充我們想要獲得的使用方法,我們可以通過自定義兩類attribute:
一類是針對插件接口名稱的定義,用于定義插件接口的名稱,搜尋方法等
另一類是定義實作接口的插件的規約,定義插件的名稱,資訊和接口限制
自定義可由項目需求進行,具體請參考相關文檔,此處并不打算具體說明。本定義中,mainkind字段用于存儲該插件類型,這在一些搜尋方法中是必要的。 而myresource字段可儲存目前類的資源uri。這在wpf程式中尤為有效,在程式集中可嵌入圖形,音樂甚至視訊資源,可提供更好的使用者體驗。 而detailinfo字段可儲存對該插件的一些文字性描述。
下面展示該attribute的使用方法
其四個構造函數的參數分别是:名稱,類型(一般是實作的接口),類說明,資源名稱。
3. 主程式架構動态查找可用插件的方法
主程式架構如何獲知目前插件的定制資訊,并在需要的時候執行個體化呢? 我們将儲存插件實作的atrribute辨別,需要特别注意的是type: 我們在此處存儲了該類的type,使得能在需要的時候執行個體化之。
那麼,如何執行插件搜尋呢? 網上已經有大量的說明和介紹。 不外乎是搜尋某一程式集,擷取所有類型type,并查詢其是否實作了某類接口。 但當工程中有不止一種插件類型時,我們就該考慮實作代碼複用:
可以定義一個特殊的類singletonprovider,考慮到該類功能較為固定,采用單例模式實作。 在内部,給出了一個靜态字典和兩個靜态方法:
static dictionary<string, observablecollection<objectbasicinfo>> myobjectbasicinfodictionary: 插件字典:使用可通知集合observablecollection作為字典值,字典key是接口名稱(再次聲明,此接口非一般意義上的接口,而是某種對插件分類的某種定義或限制,當然,可以使用c#的接口實作) 使用observablecollection僅僅是因為它在集合項目更改時可提供通知,友善wpf的資料綁定,如果不需要此項,你完全可以修改成你想要的集合類型如list
public static observablecollection<objectbasicinfo> getinstance(string interfacename) : 查詢被某種接口限制的所有插件方法:
具體資訊可以檢視具體代碼, 當字典中已經存在該接口插件集合,則直接傳回結果,否則執行查詢。需要注意:assembly assembly = assembly.getcallingassembly(); 可獲得目前被調用的程式集,這就保證了查找不同接口時是在儲存目前插件程式集的dll上動态執行的,這點非常重要,就可不用提供程式集名稱。 另外,當找到某一滿足需求的type時,就可以查找目前type所有的attribute,并擷取資訊所有儲存至字典中。 在下次調用同樣接口的插件清單時,就可以不用再次執行查找。
public static object getobjectinstance(string interfacename, int index) 封裝了的反射執行個體化方法。 類型參數是接口名稱和在插件清單中的位置,為了簡化, 類的構造函數必須是沒有形參的。
下面的代碼做了詳細的說明:
使用時非常友善,下面我們會介紹如何使用他。
4. 使用方法
下面我們以一個具體場景介紹這一方法的使用: 假設有一個通信系統,要求動态增減通信功能,如usb,序列槽,wifi,藍牙等。 我們将其公共方法為接口icommmethod ,所有通信方法都必須實作這一接口,并增加自定義的xfrmworkattribute.
icommmethod的接口規範标記如下:
其他方法不一一列舉。
我們在一個插件管理類中直接這樣調用:
commethodlist.datacontext = singletonprovider.getinstance("icommmethod");
commethodlist是我的系統中一個可用通信方法清單的wpf的listbox控件。 直接将 observablecollection<objectbasicinfo>> 集合綁定到該控件的資料上下文中,即可顯示目前所有的通信清單。 listbox中的對應index即插件集合的index,通過上面描述的執行個體化方法,就能反射執行個體化之。 讨論wpf的資料綁定超過了本文的範疇,但可查詢相關資料。讀者可以自行設計和使用該集合提供的資料。顯示效果如下圖:
5. 在已執行個體化的對象中擷取該類的attribute資料
還有一個遺留問題,即在執行個體化的對象中,我們依舊要獲得類的名稱,描述和其他相關資訊,如何做呢? 我們定義如下的attribute方法實作之
若該類未實作自定義的attribute,傳回一個“空”值,提醒設計者或開發人員。
使用起來也很簡單,例如我們想獲得該對象的“名稱”:
注意,屬性通路器中,顯然隻應該實作get方法,它是運作時的不可修改對象。
6. 其他
本文的來源是作者項目中的實際需要,不執行個體化類而獲得類的相關資訊是一種很普遍的需求,attribute是一種做法,也可以通過xml實作,很多插件系統就是這麼做的,但對于輕量級的系統來說,attribute可能更合适,而單例模式的引用給該方法帶來了很大的友善。 有任何問題歡迎随時留言交流。