一、前言
MEF(Managed Extensibility Framework),是輕量級的外掛框架。使用簡單,功能強大。詳細介紹見MSDN,本文不再贅述。
在使用MEF時,會遇到這樣一種場景:
主程式和外掛都引用了同一個DLL中同一個【方法F】,但是引用的DLL版本不一致。
那麼,程式在執行時,會出現4種情況:
(注:文字描述不太直觀,可參照下節的實際演示)
1,不同版本DLL中【方法F】未做改變:外掛可正常呼叫【方法F】。
2,不同版本DLL中【方法F】內部實現做了改變:引用了與主程式所引用的DLL版本不一致的外掛,在呼叫【方法F】時,呼叫的不是外掛所引用的DLL版本中的【方法F】,而是呼叫的主程式所引用的DLL版本中的【方法F】。
3,不同版本DLL中【方法F】增加了過載方法:如果主程式所引用的DLL版本是包含過載方法的(即主程式所引用的DLL版本比外掛引用的DLL版本新),那麼外掛都可以正常呼叫,不過呼叫的【方法F】來自主程式所引用的DLL版本中的【方法F】;相反,如果主程式所引用的DLL版本是未包含過載方法的(即主程式所引用的DLL版本比外掛引用的DLL版本舊),那麼,那些呼叫了【過載方法F】的外掛在執行時將會報錯。
4,不同版本DLL中【方法F】發生了改變——增減引數、改變返回值型別、刪除了方法等:引用了與主程式所引用的DLL版本不一致的外掛,在呼叫【方法F】時會報錯。
本篇文章,就是來講一下如何實現主程式與外掛們各自呼叫各自版本的DLL,互不影響且正常呼叫的。
相信看完的你,一定會有所收穫!
本文地址:https://www.cnblogs.com/lesliexin/p/16280161.html
二、問題復現
(一)程式碼結構
為了方便演示,我們建立一個簡單的MEF程式,其結構如下:
其中:
1,介面
介面類很簡單,包含一個介面定義和一個自定義的MEF匯出特性標籤。
a,介面定義中,只包含了一個方法:Run();
b,自定義的MEF匯出特性標籤,主要是為MEF外掛新增一個標記,方便對應到具體外掛。
2,公共DLL
公共DLL類,即復現場景時,主程式和外掛們都需要引用的類。
因為要生成不同版本的公共DLL,所以我們依次修改程式碼,並在生成屬性中設定版本號,然後編譯生成DLL。
依次修改4次,共計4個版本的公共DLL:v1.0、v2.0、v3.0、v4.0,其程式碼修改如下:
v1.0:
v2.0:
v3.0:
v4.0:
3,外掛
這些外掛除了引用的公共DLL版本不一致外,基本方法都是一樣的:繼承並實現介面。
這裡由於“公共DLL v3.0”中對方法進行了過載,所以我們這裡用兩個外掛來分別呼叫每一方法。
“外掛1”程式碼:
“外掛2”程式碼:
“外掛3”程式碼:
“外掛4”程式碼:
“外掛5”程式碼:
4,主程式
主程式的介面設計如下:
其中:
“主程式”按鈕作用:直接呼叫公共DLL中的方法。
“外掛1” - “外掛4” 按鈕作用:呼叫外掛中的方法。
因為要復現場景,所以主程式也需要生成多個版本。
又因為在公共DLL v3.0中過載了方法,所以多分一個版本,來分別呼叫這兩個方法。
所以最終會生成5個版本的主程式。
最終生成的檔案結構如下:
主程式的程式碼,主要分為3部分:
3.1,載入MEF外掛
在程式啟動時,我們需要載入所有外掛。
3.2,呼叫外掛方法
因為所有的外掛都是基於統一的介面,所以我們先寫一個通用的呼叫外掛方法,然後在點選按鈕時,直接傳入外掛對應匯出標記即可。
3.3,主程式呼叫公共DLL方法
對公共DLL的方法呼叫與外掛並無二致,5個版本的主程式,其程式碼變化如下:
v1.0:
v2.0:
v3.0:
v4.0
v5.0:
(二)演示
我們依次編譯生成不同版本的主程式,然後依次執行,其結果如下:
v1.0:
v2.0:
v3.0:
v4.0:
v5.0:
可以發現,當月主程式與外掛都引用相同的公共DLL後,無論外掛引用的公共DLL版本是多少,呼叫的均是主程式所引用的公共DLL版本。
當外掛與主程式引用的公共DLL中方法發生改變(如增減引數、修改返回值、刪除了方法等),外掛將會呼叫失敗,丟擲異常。
三、解決方案
問題已復現,那麼我們該如何解決呢?
解決目標就是主程式與外掛們,各自呼叫各自所引用版本的“公共DLL”。在本示例中,就是:外掛1引用“公共DLL v1.0”,外掛2引用“公共DLL v2.0”,等等。
而解決方案非常之簡單,簡單到一句話就能說完:
為公共DLL新增強簽名
關於“強簽名”,本文不再贅述,請參考MSDN。
下面,我們來對示例進行修改。
(一)新增強簽名
我們在公共DLL上右鍵,選擇“屬性”,然後選擇“簽名”,按提示新增強簽名即可。
(二)重新生成外掛和主程式。
我們依次重新生成外掛,和主程式,過程不再贅述。
(三)演示
我們重新依次執行5個版本的主程式,會發現問題已解決,主程式與外掛們都各自呼叫了自己所引用版本的公共DLL。
其與未進行強簽名時的執行結果對比如下:
v1.0:
v2.0:
v3.0:
v4.0:
v5.0:
四、總結
說實話,本篇文章所描述的問題,雖然不難,解決辦法也很簡單,卻曾經困擾了我好久。
之前一直沒有找到現成的可行解決方案,曾在博問上提問過,答案雖然有用,但無奈不知怎麼去應用。這事也就放下了。
最近在看《CLR via C#》,曾經看過,但走馬觀花、不知所云、無甚收穫。此次重看,發現已可以讀懂,頗有感獲。
在看到了強簽名時,驀然發現,這不就是曾經困擾我很久的解決方法嗎?
既心動便行動,經過一番測試,果然可行。
挺感慨的,果然往基礎的方向去學習,是正確的。
本人水平有限,難免有所疏漏,歡迎各位讀者評論指正。
五、原始碼下載
https://files.cnblogs.com/files/lesliexin/MEFDemo.zip
-【END】-