




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、第36章插 件插件可以在以后給應用程序添加功能。我們可以創(chuàng)建一個主機應用程序,隨時間的推移給它添加越來越多的功能-這些功能可以是開發(fā)團隊編寫的,其他供應商也可以創(chuàng)建插件,擴展該應用程序。目前,插件在許多不同的應用程序上使用,例如IE和Visual Studio。IE是一個主機應用程序,它提供了一個插件框架,許多公司都使用這個框架提供查看Web頁面時的擴展程序。Shockwave Flash Object可以查看帶Flash內容的Web頁面。Google工具欄提供了特殊的Google功能,可以在IE中快速訪問。Visual Studio也有一個插件模型,可以用
2、不同層次的擴展程序擴展Visual Studio。定制應用程序總是可以創(chuàng)建插件模型,以動態(tài)加載和使用程序集中的功能。利用插件模型時,需要考慮許多問題。如何檢測新的程序集?如何解決版本問題?插件可以改變主機應用程序的穩(wěn)定性嗎?.NET Framework 3.5提供了一個框架,用程序集System.AddIn來保存和創(chuàng)建插件。這個框架也稱為Managed AddIn Framework(MAF)。提示:插件還有其他稱呼,如add-on或plug-in。本章內容如下: System.AddIn體系結構 創(chuàng)建簡單的插件36.1 System.AddIn體系結構創(chuàng)建
3、允許在運行期間添加插件的應用程序時,需要處理一些問題。例如,如何找到插件,如何解決版本問題,使主機應用程序和插件可以獨立地升級。要解決這些問題,有幾種方式。本節(jié)討論插件的問題和MAF解決它們的體系結構: 插件的問題 管道體系結構 發(fā)現(xiàn) 激活 隔離 生存期 版本問題36.1.1 插件的問題要創(chuàng)建一個主機應用程序,動態(tài)加載以后添加的程序集,必須解決幾個問題,如表36-1所示。表 36-1插件問題說 明發(fā)現(xiàn)如何為主機應用程序查找新插件?這有幾個不同的選項。一個選項是在配置文件中添加插件的信息。
4、其缺點是安裝新插件時,需要修改已有的配置文件。另一個選項是把包含插件的程序集復制到預定義的目錄中,通過反射讀取程序集的信息。反射的更多內容可參見第13章激活程序集動態(tài)加載后,還不能使用new運算符創(chuàng)建它的實例。但可以用Activator類創(chuàng)建這類程序集。另外,如果插件加載到另一個應用程序域中或新進程中,還需要使用不同的激活選項。程序集和應用程序域的更多內容可參見第17章隔離插件可能會使主機應用程序崩潰,讀者可能見過IE因各種插件而崩潰的情況。根據(jù)主機應用程序的類型和插件的集成方式,插件可以加載到另一個應用程序域或另一個進程中生存期清理對象是垃圾回收器的一個工作。但是,垃圾回收器在這里沒有任何幫
5、助,因為插件可能在另一個應用程序域中或另一個進程中激活。把對象保存在內存中的其他方式有引用計數(shù)、租借和承辦機制版本版本問題是插件的一個大問題。通常主機的一個新版本仍可以加載舊插件,而舊主機應有加載新插件的選項下面探討MAF的體系結構,說明這個框架如何解決這些問題。MAF的設計目標如下: 應易于開發(fā)插件 在運行期間查找插件應很高效 開發(fā)主機程序應是一個很簡單的過程,但不像開發(fā)插件那么容易 插件和主機應用程序應獨立地升級36.1.2 管道體系結構MAF體系結構基于一個包含7個程序集的管道。這個管道解決了插件的版本問題。因為管道中的程序集之
6、間的依賴性很低,所以合同、主機程序和插件升級到新版本可以完全互不干擾。圖36-1顯示了MAF體系結構的管道。其中心是合同程序集。這個程序集包含一個合同接口,其中列出了插件必須實現(xiàn)、可以由主機程序調用的方法和屬性。合同的左邊是主機端,右邊是插件端。圖中還顯示了程序集之間的依賴性。最左端的主機程序集與合同程序集沒有依賴性,插件程序集與合同程序集也沒有依賴性,這兩個程序集都沒有實現(xiàn)合同定義的接口,只是有一個對視圖程序集的引用。主機應用程序引用主機視圖;插件引用插件視圖。視圖包含抽象的視圖類,該類定義的方法和屬性與合同相同。 圖 36-1圖36-2顯示了管道中類的關系。主機類與抽
7、象的主機視圖類有一個關聯(lián),并調用其方法。抽象的主機視圖類由主機適配器實現(xiàn)。適配器在視圖和合同之間建立連接。插件適配器實現(xiàn)了合同的方法和屬性。這個適配器包含對插件視圖的引用,把來自主機端的調用傳送給插件視圖。主機適配器類定義了一個具體的類,它派生自主機視圖的抽象基類,實現(xiàn)了方法和屬性。這個適配器包含對合同的引用,把來自視圖的調用傳送給合同。 圖 36-2 有了這個模型,插件端和主機端可以完全獨立地升級了,只是需要使用映射層。例如,如果主機的一個新版本使用全新的方法和屬性,合同就仍可以保持不變,只有適配器需要修改。也可以定義新的合同。適配器可以修改,也可以同時使用幾個合同。3
8、6.1.3 發(fā)現(xiàn)如何為主機應用程序查找新插件?MAF體系結構使用一個預定義的目錄結構來查找插件和管道的其他程序集。管道的組成部分保存在這些子目錄中: HostSideAdapters Contracts AddInSideAdapters AddInViews AddIns除了AddIns目錄之外,其他目錄都直接包含管道特定部分的程序集。AddIns目錄為每個插件程序集包含一個子目錄。插件也可以保存在完全獨立于其他管道組件的目錄中。管道的程序集需要使用反射來動態(tài)加載,才能獲得插件的所有信息。而且,對于許多插件而言,這還會增
9、加主機應用程序的啟動時間。因此,MAF使用一個高速緩存,來保存管道組件的信息。該高速緩存是由安裝插件的程序創(chuàng)建的,如果主機應用程序有管道目錄的寫入權限,該高速緩存就由主機應用程序創(chuàng)建。給管道組件高速緩存的信息是調用AddInStore類的方法來創(chuàng)建的。Update()方法查找還沒有列在保存文件中的新插件。Rebuild()方法用插件的信息重建完全二進制的保存文件。表36-2列出了AddInStore類的成員。表 36-2AddInStore成員說 明Rebuild()RebuildAddIns()Rebuild()方法為管道的所有組件重建高速緩存。如果插件存儲在另一個目錄下,就可以
10、使用RebuildAddIns()重建插件的高速緩存Update()UpdateAddIns()Rebuild()方法重建管道的完整高速緩存,Update()方法只用新管道組件更新高速緩存。UpdateAddIns()方法只更新插件的高速緩存FindAddIn()FindAddIns()這些方法都使用高速緩存查找插件。FindAddIns()方法返回匹配主機視圖的所有插件集合。FindAddIn()方法返回一個特定的插件36.1.4 激活和隔離AddInStore類的FindAddIns()方法返回表示插件的AddInToken對象集合。使用AddInToken類可以訪問插件的信息
11、,例如名稱、描述、發(fā)布者和版本。使用Activate()方法可以激活插件。表36-3列出了AddInToken類的屬性和方法。表 36-3AddInToken成員說 明Name、Publisher、Version、DescriptionAddInToken類的Name、Publisher、Version和Description屬性返回用特性AddInAttribute賦予插件的信息AssemblyNameAssemblyName返回包含插件的程序集名稱EnableDirectConnect使用EnableDirectConnect屬性可以設置一個值,主機程序應使用該值直接連接到插件
12、上,而不使用管道的組件。只有插件和主機程序運行在同一個應用程序域,插件視圖和主機視圖的類型相同時,才能使用這個屬性。該屬性仍要求管道的所有組件都存在QualificationData插件可以用特性QualificationDataAttribute標記應用程序域和安全需求。插件可以列出安全需求和隔離需求。例如,QualificationData (“Isolation”, ”NewAppDomain”)表示插件必須保存在新進程中??梢詮腁ddInToken中讀取這些信息,激活有特定需求的插件。除了應用程序域和安全需求之外,還可以使用這個特性通過管道傳送定制信息Activate()插件用Acti
13、vate()方法激活,利用這個方法的參數(shù),可以定義插件是否加載到新應用程序域或新進程中。還可以定義插件獲得的權限一個插件可能使整個應用程序崩潰,例如IE可能因一個失敗的插件而崩潰。根據(jù)應用程序類型和插件的類型,可以讓插件運行在另一個應用程序域或另一個進程中,來避免這個問題。MAF給出了幾個選項??梢栽谛聭贸绦蛴蚧蛐逻M程中激活插件。新應用程序域還可以有有限的權限。AddInToken類的Activate()方法有幾個重載版本,在這些版本中,可以傳送加載插件的環(huán)境參數(shù)。表36-4列出了不同的選項。表 36-4AddInToken.Activate()的參數(shù)說 明AppDomain可以
14、傳送一個加載插件的新應用程序域,這樣可以使插件獨立于主機應用程序,還可以從應用程序域中卸載插件AddInSecurityLevel如果插件應使用不同的安全級別來運行,就傳送枚舉AddInSecurityLevel的一個值,其值可以是Internet、Intranet、FullTrust和HostPermissionSet如果預定義的安全級別不夠安全,還可以給插件的應用程序域賦予PermissionSetAddInProcess插件還可以運行在與主機應用程序不同的進程中。可以給Activate()方法傳送一個新的AddInProcess。如果所有的插件都卸載了,新進程就可以退出,否則新進程就繼續(xù)
15、運行。這個選項可以用KeepAlive屬性設置AddInEnvironment傳送AddInEnvironment對象是定義加載插件的應用程序域的另一個選項。在AddInEnvironment的構造函數(shù)中,可以傳送一個AppDomain對象。還可以用AddInController類的AddInEnvironment屬性獲得插件的已有AddInEnvironment提示:應用程序域詳見第17章。應用程序的類型也會限制可以使用的選項。WPF插件目前不支持跨進程。Windows Forms不能在不同的應用程序域之間連接Windows控件。下面列出調用AddInToken的Activate()方法時管
16、道的執(zhí)行步驟:(1) 用指定的權限創(chuàng)建應用程序域。(2) 用Assembly.LoadFrom()方法把插件的程序集加載到新的應用程序域中。(3) 用反射調用插件的默認構造函數(shù)。因為插件派生于在插件視圖中定義的基類,所以也加載了視圖的程序集。(4) 接著構造插件端適配器的一個實例。插件的實例傳送給適配器的構造函數(shù),使適配器能連接合同和插件。插件適配器派生于基類MarshalByRefObject,所以可以在應用程序域之間調用。(5) 激活代碼給主機應用程序的應用程序域返回插件端適配器的一個代理。插件適配器實現(xiàn)了合同接口,所以該代理包含合同接口的方法和實現(xiàn)。(6) 主機端適配器的實例在主機應用程
17、序的應用程序域中構造。插件端適配器的代理傳送給該構造函數(shù)。激活代碼會從插件令牌中查找主機端適配器的類型。主機端適配器返回給主機應用程序。36.1.5 合同合同定義了主機端和插件端之間的界限。合同用一個接口來定義,該定義必須派生于基接口IContract。合同必須仔細考慮,因為它根據(jù)需要支持靈活的插件場景。合同沒有版本支持,不能改變,所以插件以前的實現(xiàn)代碼仍可以在新的主機程序中運行。新版本應通過定義新合同來創(chuàng)建。合同的類型有一些限制,其原因是版本問題,而且應用程序域要從主機應用程序跨越到插件上。類型必須是安全的,且支持版本,能在邊界(應用程序域或跨進程)之間傳送,也能在主機程序和插件
18、之間傳送??梢杂煤贤瑐魉偷念愋涂梢允牵?#160; 基本類型 其他合同 可串行化的系統(tǒng)類型 簡單的可串行化定制類型,包括基本類型、合同,以及沒有實現(xiàn)代碼的類型接口IContract的成員如表36-5所示。表 36-5IContract的成員說 明QueryContract()使用QueryContract()可以查詢合同,驗證是否也實現(xiàn)了另一個合同。插件可以支持幾個合同RemoteToString()QueryContract()的參數(shù)需要合同的字符串表示。RemoteToString()返回當前合同的字符串表示AcquireLifetimeTo
19、ken()RevokeLifetimeToken()客戶機調用AcquireLifetimeToken()來保存對合同的引用。AcquireLifetime- Token()會遞增引用計數(shù)。RevokeLifetimeToken()遞減引用計數(shù)RemoteEquals()RemoteEquals()可用于比較兩個合同引用合同接口在System.AddIn.Contract、System.AddIn.Contract.Collections和System.AddIn. Contract.Automation命名空間中定義。表36-6列出了可以用于合同的合同接口。表 36-6合 同說
20、明IListContract<T>IListContract<T>可用于返回一個合同列表IEnumeratorContract<T>IEnumeratorContract<T>用于枚舉IListContract<T>的元素IServiceProviderContract一個插件可以為其他插件提供服務。提供服務的插件稱為服務提供程序,它實現(xiàn)了接口IServiceProviderContract。通過QueryService()方法,可以查詢實現(xiàn)該接口的插件提供了什么服務(續(xù)表) 合 同說 明IProfferServiceContract
21、IProfferServiceContract是服務提供程序和IServiceProviderContract提供的接口。IProfferServiceContract定義了方法ProfferService()和Revoke Service()。ProfferService()給所提供的服務添加了一個IServiceProvider Contract,而RevokeService()刪除它INativeHandleContract這個接口允許使用GetHandle()方法訪問內部的Windows句柄。這個合同由WPF主機程序用于使用WPF插件36.1.6 生存期插件需要加載多長時間
22、?使用多少時間?何時可以卸載應用程序域?這有幾個選項。一個選項是使用引用計數(shù)。每次使用插件都會遞增引用計數(shù)。如果引用計數(shù)遞減到0,就可以卸載插件。另一個選項是使用垃圾回收器。如果垃圾回收器在運行,且沒有對對象的引用,該對象就是垃圾回收器的目標。.NET Remoting使用了租約機制,是使對象保持激活狀態(tài)的承辦者。只要租期到了,就詢問承辦者該對象是否應繼續(xù)保持激活狀態(tài)。卸載插件還有一個特殊的問題,因為插件運行在不同的應用程序域、不同的進程中。但垃圾回收器不能跨進程工作。MAF使用一個混合的模型來管理生存期。在單個應用程序域中,使用垃圾回收機制。在管道內部使用一個隱式的承辦機制,但引用計數(shù)可用于
23、從外部控制承辦者。下面考慮一種情況:插件加載到另一個應用程序域中。在主機應用程序中,當不再需要引用時,垃圾回收器清理了主機視圖和主機端適配器。而在插件端,合同定義了方法AcquireLifetimeToken()和RevokeLifetimeToken(),來遞增和遞減承辦者的引用計數(shù)。這兩個版本不僅遞增和遞減一個值,還可以在某個團體頻繁調用RevokeLifetimeToken()方法時,提早釋放對象。而AcquireLifetimeToken()返回一個表示生存期令牌的標識符,這個標識符必須用于調用RevokeLifetimeToken()方法。所以這兩個方法總是成對調用。通常不必處理Ac
24、quireLifetimeToken()和RevokeLifetimeToken()方法的調用,而可以使用ContractHandle類,在構造函數(shù)中調用 AcquireLifetimeToken(),在終結器中調用RevokeLifetimeToken()。提示:終結器詳見第12章。在插件加載到新應用程序域的情形中,當不再需要插件時,可以刪除加載的代碼。MAF使用一個簡單的模型把一個插件定義為應用程序域的擁有者,如果不再需要這個插件,就卸載應用程序域。如果在激活插件時創(chuàng)建了應用程序域,這個插件就是應用程序域的擁有者。如果應用程序域是以前創(chuàng)建的,就不會自動卸載。ContractHandle類在
25、主機端適配器中用來增加插件的引用計數(shù)。這個類的成員如表36-7所示。表 36-7ContractHandle成員說 明Contract在ContractHandle類的構造過程中,可以指定一個實現(xiàn)了IContract的對象,來保存對它的引用。Contract屬性返回這個對象Dispose()Dispose()方法可以調用,而不是等待垃圾回收器執(zhí)行清理操作,撤回生存期令牌AppDomainOwner()AppDomainOwner()是ContractHandle類的一個靜態(tài)方法,如果插件擁有該方法傳送的應用程序域,該方法就返回插件適配器ContractOwnsAppDomain()
26、使用靜態(tài)方法ContractOwnsAppDomain(),可以驗證指定的合同是否是應用程序域的擁有者。如果是,在刪除合同時,會卸載應用程序域36.1.7 版本問題版本問題是插件的一個大問題。主機應用程序可以利用插件進一步開發(fā)。插件的一個要求是主機應用程序的新版本仍可以加載插件的舊版本。舊主機程序仍可以運行插件的新版本。那么,合同該如何修改呢?System.AddIn完全獨立于主機應用程序和插件的實現(xiàn),這是通過包含7部分的管道概念實現(xiàn)的。36.2 插件示例下面是一個主機應用程序的簡單示例,它可以加載計算器插件。插件支持不同的計算操作。我們需要創(chuàng)建一個解決方案,它包含6個
27、庫項目和一個控制臺應用程序。示例應用程序的項目如表36-8所示。這個表列出了需要引用的程序集。在解決方案中引用了其他項目后,還需要把Copy Local屬性設置為False,這樣程序集就不會復制。但HostApp控制臺項目例外,它需要對HostView項目的引用。這個程序集必須復制,才能在主機應用程序中找到。另外,還需要修改所生成的程序集的輸出路徑,使程序集復制到管道的正確目錄下。表 36-8項 目引 用輸 出 路 徑說 明CalcContractSystem.AddIn.Contract.PipelineContracts這個程序集包含與插件通信的合同。合同用接口定義CalcVi
28、ewSystem.AddIn .PipelineAddInViewsCalcView程序集包含一個由插件引用的抽象類,這是合同的插件端CalcAddInSystem.AddIn.CalcView.PipelineAddIns CalcAddInCalcAddIn是引用插件視圖程序集的插件項目。這個程序集包含插件的實現(xiàn)代碼(續(xù)表) 項 目引 用輸 出 路 徑說 明CalcAddInAdapterSystem.AddInSystem.AddIn.ContractCalcViewCalcContract.PipelineAddInSideAdaptersCalcAddInAdapter連接插件視圖和
29、合同程序集,把合同映射到插件視圖上HostView包含主機視圖的抽象類的程序集不需要引用任何插件程序集,也沒有解決方案中的其他項目引用HostAdapterSystem.AddInSystem.AddIn.ContractHostViewCalcContract.Pipeline HostSideAdapters主機適配器把主機視圖映射到合同上。因此需要引用這些項目HostAppSystem.AddInHostView主機應用程序激活插件36.2.1 計算器合同下面實現(xiàn)合同程序集。合同程序集包含一個合同接口,該接口定義了在主機程序和插件之間通信的協(xié)議。下面的代碼是為計算器示例應用程
30、序定義的合同。應用程序給合同定義了方法GetOperations()和Operate()。GetOperations()方法返回計算器插件支持的一組數(shù)學操作。數(shù)學操作是由IOperationContract接口定義的,IOperationContract接口本身就是一個合同,定義了只讀屬性Name和NumberOperands。Operate()方法調用插件中的操作,它需要IOperation接口定義的一個操作和通過double數(shù)組提供的操作數(shù)。有了這個接口,插件就支持需要任意多個double操作數(shù)的操作,且返回一個double。屬性AddInContract由AddInStore用于建立高速
31、緩存。這個屬性把類標記為插件合同接口。using System.AddIn.Contract;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsAddInContractpublic interface ICalculatorContract : IContractIListContract < IOperationContract > GetOperations();double Operate(IOperationContract operation, double operands);public inter
32、face IOperationContract : IContractstring Name get; int NumberOperands get; 36.2.2 計算器插件視圖插件視圖重新定義了插件眼中的合同。該合同定義了接口ICalculatorContract和IOperationContract。為此,插件視圖定義了抽象類Calculator和具體的類Operation。在Operation中,沒有每個插件都需要的特定實現(xiàn)代碼,因為該類已經用插件視圖程序集實現(xiàn)了。這個類用屬性Name和NumberOperands描述了數(shù)學計算的一個操作。抽象類Calculator定義了需
33、要由插件實現(xiàn)的方法。雖然合同定義了需要在應用程序域和進程之間傳送的參數(shù)和返回類型,但插件視圖不需要。這里可以使用類型,以便于插件開發(fā)人員編寫插件。GetOperations()方法返回IList<Operation>,而不是IlistOperation<IOperationContract>,這與合同程序集不同。AddInBase屬性把類標識為插件視圖,用于存儲。using System.AddIn.Pipeline;using System.Collections.Generic;namespace Wrox.ProCSharp.AddInsAddInBasepubl
34、ic abstract class Calculatorpublic abstract IList < Operation > GetOperations();public abstract double Operate(Operation operation, double operand);public class Operationpublic string Name get; set; public int NumberOperands get; set; 36.2.3 計算器插件適配器插件適配器把合同映射到插件視圖上。這個程序集引用了合同和插件視圖程序集。適配
35、器的實現(xiàn)代碼需要把合同中的方法IListContract<IOperationContract> GetOperations()映射到視圖方法IList<Operation> GetOperations()上。該程序集包含類OperationViewToContractAddInAdapter和CalculatorViewToContract- AddInAdapter。這兩個類實現(xiàn)了接口IOperationContract和ICalculatorContract?;涌贗Contract的方法可以通過派生于基類ContractBase來實現(xiàn)。這個基類提供了默認的實現(xiàn)代
36、碼。OperationViewToContractAddInAdapter實現(xiàn)了IOperationContract接口的其他成員,并把調用傳送給在構造函數(shù)中指定的Operation View。類OperationViewToContractAddInAdapter還包含靜態(tài)幫助方法ViewToContractAdapter()和ContractToViewAdapter(),前者把Operation映射到IOperationContract上,后者把IOperationContract映射到Operation上。using System.AddIn.Pipeline;namespace Wr
37、ox.ProCSharp.AddInsinternal class OperationViewToContractAddInAdapter : ContractBase,IOperationContractprivate Operation view;public OperationViewToContractAddInAdapter(Operation view)this.view = view;public string Nameget return view.Name; public int NumberOperandsget return view.NumberOperands; pu
38、blic static IOperationContract ViewToContractAdapter(Operation view)return new OperationViewToContractAddInAdapter(view);public static Operation ContractToViewAdapter(IOperationContract contract)return (contract as OperationViewToContractAddInAdapter).view;類CalculatorViewToContractAddInAdapter非常類似于O
39、perationViewToContractAddIn- Adapter:它派生自ContractBase,繼承了IContract接口的默認實現(xiàn)代碼,還實現(xiàn)了一個合同接口。但ICalculatorContract接口是用GetOperations()和Operate()方法實現(xiàn)的。適配器的Operate()方法調用視圖類Calculator的Operate()方法,其中IOperationContract需要轉換為Operation。這是使用類OperationViewToContractAddInAdapter的靜態(tài)幫助方法ContractViewToAdapter()完成的。GetOpe
40、rations()方法的實現(xiàn)需要把集合IListContract<IOperationContract>轉換為IList<Operation>。對于這個集合轉換,類CollectionAdapters定義了轉換方法ToIList()和ToIListContract()。其中ToIListContract()方法用于這個轉換。屬性AddInAdapter把類標識為插件端適配器,用于插件的存儲。using System.AddIn.Contract;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsAddI
41、nAdapterinternal class CalculatorViewToContractAddInAdapter : ContractBase,ICalculatorContractprivate Calculator view;public CalculatorViewToContractAddInAdapter(Calculator view)this.view = view;public IListContract < IOperationContract > GetOperations()return CollectionAdapters.ToIListContrac
42、t < Operation,IOperationContract > (view.GetOperations(),OperationViewToContractAddInAdapter.ViewToContractAdapter,OperationViewToContractAddInAdapter.ContractToViewAdapter);public double Operate(IOperationContract operation, double operands)return view.Operate(OperationViewToContractAddInAdap
43、ter.ContractToViewAdapter(operation), operands);提示:因為適配器類是由.NET反射功能調用的,所以這些類可以使用內部的訪問修飾符。這些類是要具體實現(xiàn)的,所以最好使用internal訪問修飾符。36.2.4 計算器插件插件現(xiàn)在包含具體的實現(xiàn)代碼。它是用類CalculatorV1實現(xiàn)的。插件程序集依賴于插件視圖程序集,因為它需要實現(xiàn)抽象類Calculator。屬性AddIn把類標記為插件,用于插件的存儲,并添加了發(fā)布者、版本和描述信息。在主機端,這些信息可以從AddInToken中訪問。CalculatorV1在方法GetOperatio
44、ns()中返回一組支持的操作。Operate()方法根據(jù)操作計算操作數(shù)。using System;using System.AddIn;using System.Collections.Generic;namespace Wrox.ProCSharp.AddInsAddIn("CalculatorAddIn", Publisher="Wrox Press", Version="",Description="Sample AddIn")public class CalculatorV1 : Calcul
45、atorprivate List < Operation > operations;public CalculatorV1()operations = new List < Operation > ();operations.Add(new Operation() Name = "+", NumberOperands = 2 );operations.Add(new Operation() Name = "-", NumberOperands = 2 );operations.Add(new Operation() Name =
46、"/", NumberOperands = 2 );operations.Add(new Operation() Name = "*", NumberOperands = 2 );public override IList < Operation > GetOperations()return operations;public override double Operate(Operation operation, double operand)switch (operation.Name)case "+":return
47、 operand0 + operand1;case "-":return operand0 - operand1;case "/":return operand0 / operand1;case "*":return operand0 * operand1;default:throw new InvalidOperationException(String.Format("invalid operation 0", operation.Name);36.2.5 計算器主機視圖下面看看主機端的主機視圖。與
48、插件視圖類似,主機視圖也定義了一個抽象類,其方法類似于合同。但是,這里定義的方法是由主機應用程序調用的。類Calculator和Operation都是抽象的,因為其成員由主機適配器實現(xiàn)。它們只需要定義由主機應用程序使用的接口:using System.Collections.Generic;namespace Wrox.ProCSharp.AddInspublic abstract class Calculatorpublic abstract IList < Operation > GetOperations();public abstract double Operate(Op
49、eration operation,params double operand);public abstract class Operationpublic abstract string Name get; public abstract int NumberOperands get; 36.2.6 計算機主機適配器主機適配器程序集引用了主機視圖和合同,把視圖映射到合同上。類OperationContractToViewHostAdapter實現(xiàn)了抽象類Operation的成員。CalculatorContractTo- ViewHostAdapter實現(xiàn)了抽象類Calculat
50、or的成員。在OperationContractToViewHostAdapter中,在構造函數(shù)中指定了對合同的引用。適配器類還包含一個ContractHandle實例,它添加了對合同的生存期引用,所以只要主機應用程序需要,插件就保持加載狀態(tài)。using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsinternal class OperationContractToViewHostAdapter : Operationprivate ContractHandle handle;public IOperationContract Co
51、ntract get; private set; public OperationContractToViewHostAdapter(IOperationContract contract)this.Contract = contract;handle = new ContractHandle(contract);public override string Namegetreturn Contract.Name;public override int NumberOperandsgetreturn Contract.NumberOperands;internal static class O
52、perationHostAdaptersinternal static IOperationContract ViewToContractAdapter(Operation view)return (OperationContractToViewHostAdapter)view).Contract;internal static Operation ContractToViewAdapter(IOperationContract contract)return new OperationContractToViewHostAdapter(contract);類CalculatorContrac
53、tToViewHostAdapter實現(xiàn)了抽象主機視圖類Calculator的成員,并把調用傳送給合同。該類還有一個ContractHandle實例,它包含對合同的引用,這類似于插件端類型轉換的適配器。但這次僅需要從插件適配器向主機端的類型轉換。屬性HostAdapter把類標記為一個需要在HostSideAdapters目錄下安裝的適配器。using System.Collections.Generic;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsHostAdapterinternal class Calculator
54、ContractToViewHostAdapter : Calculatorprivate ICalculatorContract contract;private ContractHandle handle;public CalculatorContractToViewHostAdapter(ICalculatorContract contract)this.contract = contract;handle = new ContractHandle(contract);public override IList < Operation > GetOperations()ret
55、urn CollectionAdapters.ToIList < IOperationContract, Operation > (contract.GetOperations(),OperationHostAdapters.ContractToViewAdapter,OperationHostAdapters.ViewToContractAdapter);public override double Operate(Operation operation, double operands)return contract.Operate(OperationHostAdapters.
56、ViewToContractAdapter(operation), operands);36.2.7 計算器主機示例主機應用程序使用了WPF技術。這個應用程序的用戶界面如圖36-3所示。其頂部是可用的插件列表。左邊是活動插件的操作。選擇要調用的操作時,就會顯示操作數(shù)。輸入了操作數(shù)的值后,就可以調用插件的操作。底部的按鈕用于重建和更新插件存儲器,以及退出應用程序。 圖 36-3下面的XAML代碼顯示了用戶界面的樹形結構。在ListBox元素中,給項模板使用不同的樣式,為插件列表、操作列表和操作數(shù)列表指定特定的表示方式。< DockPanel ><
57、; GroupBox Header="AddIn Store" DockPanel.Dock="Bottom" >< UniformGrid Columns="4" >< Button x:Name="rebuildStore" Click="RebuildStore"Margin="5" > Rebuild < /Button >< Button x:Name="updateStore" Click=&qu
58、ot;UpdateStore"Margin="5" > Update < /Button >< Button x:Name="refresh" Click="RefreshAddIns"Margin="5" > Refresh < /Button >< Button x:Name="exit" Click="App_Exit" Margin="5" > Exit < /Button &g
59、t;< /UniformGrid >< /GroupBox >< GroupBox Header="AddIns" DockPanel.Dock="Top" >< ListBox x:Name="listAddIns" ItemsSource="Binding"Style="StaticResource listAddInsStyle" / >< /GroupBox >< GroupBox DockPanel.Dock="Left" Header="Operations"
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 自愿離婚合同協(xié)議書
- 咨詢服務外包合同
- 客戶反饋處理流程表格化展示
- 2025年廣州房產中介合同6篇
- 2025年遼寧貨運車從業(yè)考試題
- 合同協(xié)議-汽車有限公司集體合同6篇
- 防火門承攬加工合同格式6篇
- 建材供貨合同7篇
- 保稅器材維修合同范本
- 包銷合同范本
- 江蘇農牧科技職業(yè)學院單招《職業(yè)技能測試》參考試題庫(含答案)
- 小學勞動教育二年級下冊教學計劃
- 三年級上冊脫式計算100題及答案
- 2024春開學第一課-開學第一課 禁毒我先行 課件
- 《聽歌識曲》課件
- 金屬冶煉安全培訓課件
- 采血護士培訓課件
- 140m集裝箱船船體說明書
- 高等教育學課件-
- 送達地址確認書
- 機動車檢測站管理制度
評論
0/150
提交評論