天天看點

.NET插件系統之二——不執行個體化擷取插件資訊和可視化方法

 面臨的問題

      在開發插件系統中,我們通常會面臨這樣的問題:

       一些功能并不是在開啟時就要被使用的,例如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的資料綁定超過了本文的範疇,但可查詢相關資料。讀者可以自行設計和使用該集合提供的資料。顯示效果如下圖:

.NET插件系統之二——不執行個體化擷取插件資訊和可視化方法

     5. 在已執行個體化的對象中擷取該類的attribute資料

        還有一個遺留問題,即在執行個體化的對象中,我們依舊要獲得類的名稱,描述和其他相關資訊,如何做呢?  我們定義如下的attribute方法實作之

    若該類未實作自定義的attribute,傳回一個“空”值,提醒設計者或開發人員。 

        使用起來也很簡單,例如我們想獲得該對象的“名稱”:

   注意,屬性通路器中,顯然隻應該實作get方法,它是運作時的不可修改對象。

   6. 其他

      本文的來源是作者項目中的實際需要,不執行個體化類而獲得類的相關資訊是一種很普遍的需求,attribute是一種做法,也可以通過xml實作,很多插件系統就是這麼做的,但對于輕量級的系統來說,attribute可能更合适,而單例模式的引用給該方法帶來了很大的友善。 有任何問題歡迎随時留言交流。