




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第36章插 件插件可以在以后给应用程序添加功能。我们可以创建一个主机应用程序,随时间的推移给它添加越来越多的功能-这些功能可以是开发团队编写的,其他供应商也可以创建插件,扩展该应用程序。目前,插件在许多不同的应用程序上使用,例如IE和Visual Studio。IE是一个主机应用程序,它提供了一个插件框架,许多公司都使用这个框架提供查看Web页面时的扩展程序。Shockwave Flash Object可以查看带Flash内容的Web页面。Google工具栏提供了特殊的Google功能,可以在IE中快速访问。Visual Studio也有一个插件模型,可以用不同层次的扩展程序扩展Visual Studio。定制应用程序总是可以创建插件模型,以动态加载和使用程序集中的功能。利用插件模型时,需要考虑许多问题。如何检测新的程序集?如何解决版本问题?插件可以改变主机应用程序的稳定性吗?.NET Framework 3.5提供了一个框架,用程序集System.AddIn来保存和创建插件。这个框架也称为Managed AddIn Framework(MAF)。提示:插件还有其他称呼,如add-on或plug-in。本章内容如下: System.AddIn体系结构 创建简单的插件36.1 System.AddIn体系结构创建允许在运行期间添加插件的应用程序时,需要处理一些问题。例如,如何找到插件,如何解决版本问题,使主机应用程序和插件可以独立地升级。要解决这些问题,有几种方式。本节讨论插件的问题和MAF解决它们的体系结构: 插件的问题 管道体系结构 发现 激活 隔离 生存期 版本问题36.1.1 插件的问题要创建一个主机应用程序,动态加载以后添加的程序集,必须解决几个问题,如表36-1所示。表 36-1插件问题说 明发现如何为主机应用程序查找新插件?这有几个不同的选项。一个选项是在配置文件中添加插件的信息。其缺点是安装新插件时,需要修改已有的配置文件。另一个选项是把包含插件的程序集复制到预定义的目录中,通过反射读取程序集的信息。反射的更多内容可参见第13章激活程序集动态加载后,还不能使用new运算符创建它的实例。但可以用Activator类创建这类程序集。另外,如果插件加载到另一个应用程序域中或新进程中,还需要使用不同的激活选项。程序集和应用程序域的更多内容可参见第17章隔离插件可能会使主机应用程序崩溃,读者可能见过IE因各种插件而崩溃的情况。根据主机应用程序的类型和插件的集成方式,插件可以加载到另一个应用程序域或另一个进程中生存期清理对象是垃圾回收器的一个工作。但是,垃圾回收器在这里没有任何帮助,因为插件可能在另一个应用程序域中或另一个进程中激活。把对象保存在内存中的其他方式有引用计数、租借和承办机制版本版本问题是插件的一个大问题。通常主机的一个新版本仍可以加载旧插件,而旧主机应有加载新插件的选项下面探讨MAF的体系结构,说明这个框架如何解决这些问题。MAF的设计目标如下: 应易于开发插件 在运行期间查找插件应很高效 开发主机程序应是一个很简单的过程,但不像开发插件那么容易 插件和主机应用程序应独立地升级36.1.2 管道体系结构MAF体系结构基于一个包含7个程序集的管道。这个管道解决了插件的版本问题。因为管道中的程序集之间的依赖性很低,所以合同、主机程序和插件升级到新版本可以完全互不干扰。图36-1显示了MAF体系结构的管道。其中心是合同程序集。这个程序集包含一个合同接口,其中列出了插件必须实现、可以由主机程序调用的方法和属性。合同的左边是主机端,右边是插件端。图中还显示了程序集之间的依赖性。最左端的主机程序集与合同程序集没有依赖性,插件程序集与合同程序集也没有依赖性,这两个程序集都没有实现合同定义的接口,只是有一个对视图程序集的引用。主机应用程序引用主机视图;插件引用插件视图。视图包含抽象的视图类,该类定义的方法和属性与合同相同。图 36-1图36-2显示了管道中类的关系。主机类与抽象的主机视图类有一个关联,并调用其方法。抽象的主机视图类由主机适配器实现。适配器在视图和合同之间建立连接。插件适配器实现了合同的方法和属性。这个适配器包含对插件视图的引用,把来自主机端的调用传送给插件视图。主机适配器类定义了一个具体的类,它派生自主机视图的抽象基类,实现了方法和属性。这个适配器包含对合同的引用,把来自视图的调用传送给合同。图 36-2 有了这个模型,插件端和主机端可以完全独立地升级了,只是需要使用映射层。例如,如果主机的一个新版本使用全新的方法和属性,合同就仍可以保持不变,只有适配器需要修改。也可以定义新的合同。适配器可以修改,也可以同时使用几个合同。36.1.3 发现如何为主机应用程序查找新插件?MAF体系结构使用一个预定义的目录结构来查找插件和管道的其他程序集。管道的组成部分保存在这些子目录中: HostSideAdapters Contracts AddInSideAdapters AddInViews AddIns除了AddIns目录之外,其他目录都直接包含管道特定部分的程序集。AddIns目录为每个插件程序集包含一个子目录。插件也可以保存在完全独立于其他管道组件的目录中。管道的程序集需要使用反射来动态加载,才能获得插件的所有信息。而且,对于许多插件而言,这还会增加主机应用程序的启动时间。因此,MAF使用一个高速缓存,来保存管道组件的信息。该高速缓存是由安装插件的程序创建的,如果主机应用程序有管道目录的写入权限,该高速缓存就由主机应用程序创建。给管道组件高速缓存的信息是调用AddInStore类的方法来创建的。Update()方法查找还没有列在保存文件中的新插件。Rebuild()方法用插件的信息重建完全二进制的保存文件。表36-2列出了AddInStore类的成员。表 36-2AddInStore成员说 明Rebuild()RebuildAddIns()Rebuild()方法为管道的所有组件重建高速缓存。如果插件存储在另一个目录下,就可以使用RebuildAddIns()重建插件的高速缓存Update()UpdateAddIns()Rebuild()方法重建管道的完整高速缓存,Update()方法只用新管道组件更新高速缓存。UpdateAddIns()方法只更新插件的高速缓存FindAddIn()FindAddIns()这些方法都使用高速缓存查找插件。FindAddIns()方法返回匹配主机视图的所有插件集合。FindAddIn()方法返回一个特定的插件36.1.4 激活和隔离AddInStore类的FindAddIns()方法返回表示插件的AddInToken对象集合。使用AddInToken类可以访问插件的信息,例如名称、描述、发布者和版本。使用Activate()方法可以激活插件。表36-3列出了AddInToken类的属性和方法。表 36-3AddInToken成员说 明Name、Publisher、Version、DescriptionAddInToken类的Name、Publisher、Version和Description属性返回用特性AddInAttribute赋予插件的信息AssemblyNameAssemblyName返回包含插件的程序集名称EnableDirectConnect使用EnableDirectConnect属性可以设置一个值,主机程序应使用该值直接连接到插件上,而不使用管道的组件。只有插件和主机程序运行在同一个应用程序域,插件视图和主机视图的类型相同时,才能使用这个属性。该属性仍要求管道的所有组件都存在QualificationData插件可以用特性QualificationDataAttribute标记应用程序域和安全需求。插件可以列出安全需求和隔离需求。例如,QualificationData (“Isolation”, ”NewAppDomain”)表示插件必须保存在新进程中。可以从AddInToken中读取这些信息,激活有特定需求的插件。除了应用程序域和安全需求之外,还可以使用这个特性通过管道传送定制信息Activate()插件用Activate()方法激活,利用这个方法的参数,可以定义插件是否加载到新应用程序域或新进程中。还可以定义插件获得的权限一个插件可能使整个应用程序崩溃,例如IE可能因一个失败的插件而崩溃。根据应用程序类型和插件的类型,可以让插件运行在另一个应用程序域或另一个进程中,来避免这个问题。MAF给出了几个选项。可以在新应用程序域或新进程中激活插件。新应用程序域还可以有有限的权限。AddInToken类的Activate()方法有几个重载版本,在这些版本中,可以传送加载插件的环境参数。表36-4列出了不同的选项。表 36-4AddInToken.Activate()的参数说 明AppDomain可以传送一个加载插件的新应用程序域,这样可以使插件独立于主机应用程序,还可以从应用程序域中卸载插件AddInSecurityLevel如果插件应使用不同的安全级别来运行,就传送枚举AddInSecurityLevel的一个值,其值可以是Internet、Intranet、FullTrust和HostPermissionSet如果预定义的安全级别不够安全,还可以给插件的应用程序域赋予PermissionSetAddInProcess插件还可以运行在与主机应用程序不同的进程中。可以给Activate()方法传送一个新的AddInProcess。如果所有的插件都卸载了,新进程就可以退出,否则新进程就继续运行。这个选项可以用KeepAlive属性设置AddInEnvironment传送AddInEnvironment对象是定义加载插件的应用程序域的另一个选项。在AddInEnvironment的构造函数中,可以传送一个AppDomain对象。还可以用AddInController类的AddInEnvironment属性获得插件的已有AddInEnvironment提示:应用程序域详见第17章。应用程序的类型也会限制可以使用的选项。WPF插件目前不支持跨进程。Windows Forms不能在不同的应用程序域之间连接Windows控件。下面列出调用AddInToken的Activate()方法时管道的执行步骤:(1) 用指定的权限创建应用程序域。(2) 用Assembly.LoadFrom()方法把插件的程序集加载到新的应用程序域中。(3) 用反射调用插件的默认构造函数。因为插件派生于在插件视图中定义的基类,所以也加载了视图的程序集。(4) 接着构造插件端适配器的一个实例。插件的实例传送给适配器的构造函数,使适配器能连接合同和插件。插件适配器派生于基类MarshalByRefObject,所以可以在应用程序域之间调用。(5) 激活代码给主机应用程序的应用程序域返回插件端适配器的一个代理。插件适配器实现了合同接口,所以该代理包含合同接口的方法和实现。(6) 主机端适配器的实例在主机应用程序的应用程序域中构造。插件端适配器的代理传送给该构造函数。激活代码会从插件令牌中查找主机端适配器的类型。主机端适配器返回给主机应用程序。36.1.5 合同合同定义了主机端和插件端之间的界限。合同用一个接口来定义,该定义必须派生于基接口IContract。合同必须仔细考虑,因为它根据需要支持灵活的插件场景。合同没有版本支持,不能改变,所以插件以前的实现代码仍可以在新的主机程序中运行。新版本应通过定义新合同来创建。合同的类型有一些限制,其原因是版本问题,而且应用程序域要从主机应用程序跨越到插件上。类型必须是安全的,且支持版本,能在边界(应用程序域或跨进程)之间传送,也能在主机程序和插件之间传送。可以用合同传送的类型可以是: 基本类型 其他合同 可串行化的系统类型 简单的可串行化定制类型,包括基本类型、合同,以及没有实现代码的类型接口IContract的成员如表36-5所示。表 36-5IContract的成员说 明QueryContract()使用QueryContract()可以查询合同,验证是否也实现了另一个合同。插件可以支持几个合同RemoteToString()QueryContract()的参数需要合同的字符串表示。RemoteToString()返回当前合同的字符串表示AcquireLifetimeToken()RevokeLifetimeToken()客户机调用AcquireLifetimeToken()来保存对合同的引用。AcquireLifetime- Token()会递增引用计数。RevokeLifetimeToken()递减引用计数RemoteEquals()RemoteEquals()可用于比较两个合同引用合同接口在System.AddIn.Contract、System.AddIn.Contract.Collections和System.AddIn. Contract.Automation命名空间中定义。表36-6列出了可以用于合同的合同接口。表 36-6合 同说 明IListContractIListContract可用于返回一个合同列表IEnumeratorContractIEnumeratorContract用于枚举IListContract的元素IServiceProviderContract一个插件可以为其他插件提供服务。提供服务的插件称为服务提供程序,它实现了接口IServiceProviderContract。通过QueryService()方法,可以查询实现该接口的插件提供了什么服务(续表) 合 同说 明IProfferServiceContractIProfferServiceContract是服务提供程序和IServiceProviderContract提供的接口。IProfferServiceContract定义了方法ProfferService()和Revoke Service()。ProfferService()给所提供的服务添加了一个IServiceProvider Contract,而RevokeService()删除它INativeHandleContract这个接口允许使用GetHandle()方法访问内部的Windows句柄。这个合同由WPF主机程序用于使用WPF插件36.1.6 生存期插件需要加载多长时间?使用多少时间?何时可以卸载应用程序域?这有几个选项。一个选项是使用引用计数。每次使用插件都会递增引用计数。如果引用计数递减到0,就可以卸载插件。另一个选项是使用垃圾回收器。如果垃圾回收器在运行,且没有对对象的引用,该对象就是垃圾回收器的目标。.NET Remoting使用了租约机制,是使对象保持激活状态的承办者。只要租期到了,就询问承办者该对象是否应继续保持激活状态。卸载插件还有一个特殊的问题,因为插件运行在不同的应用程序域、不同的进程中。但垃圾回收器不能跨进程工作。MAF使用一个混合的模型来管理生存期。在单个应用程序域中,使用垃圾回收机制。在管道内部使用一个隐式的承办机制,但引用计数可用于从外部控制承办者。下面考虑一种情况:插件加载到另一个应用程序域中。在主机应用程序中,当不再需要引用时,垃圾回收器清理了主机视图和主机端适配器。而在插件端,合同定义了方法AcquireLifetimeToken()和RevokeLifetimeToken(),来递增和递减承办者的引用计数。这两个版本不仅递增和递减一个值,还可以在某个团体频繁调用RevokeLifetimeToken()方法时,提早释放对象。而AcquireLifetimeToken()返回一个表示生存期令牌的标识符,这个标识符必须用于调用RevokeLifetimeToken()方法。所以这两个方法总是成对调用。通常不必处理AcquireLifetimeToken()和RevokeLifetimeToken()方法的调用,而可以使用ContractHandle类,在构造函数中调用 AcquireLifetimeToken(),在终结器中调用RevokeLifetimeToken()。提示:终结器详见第12章。在插件加载到新应用程序域的情形中,当不再需要插件时,可以删除加载的代码。MAF使用一个简单的模型把一个插件定义为应用程序域的拥有者,如果不再需要这个插件,就卸载应用程序域。如果在激活插件时创建了应用程序域,这个插件就是应用程序域的拥有者。如果应用程序域是以前创建的,就不会自动卸载。ContractHandle类在主机端适配器中用来增加插件的引用计数。这个类的成员如表36-7所示。表 36-7ContractHandle成员说 明Contract在ContractHandle类的构造过程中,可以指定一个实现了IContract的对象,来保存对它的引用。Contract属性返回这个对象Dispose()Dispose()方法可以调用,而不是等待垃圾回收器执行清理操作,撤回生存期令牌AppDomainOwner()AppDomainOwner()是ContractHandle类的一个静态方法,如果插件拥有该方法传送的应用程序域,该方法就返回插件适配器ContractOwnsAppDomain()使用静态方法ContractOwnsAppDomain(),可以验证指定的合同是否是应用程序域的拥有者。如果是,在删除合同时,会卸载应用程序域36.1.7 版本问题版本问题是插件的一个大问题。主机应用程序可以利用插件进一步开发。插件的一个要求是主机应用程序的新版本仍可以加载插件的旧版本。旧主机程序仍可以运行插件的新版本。那么,合同该如何修改呢?System.AddIn完全独立于主机应用程序和插件的实现,这是通过包含7部分的管道概念实现的。36.2 插件示例下面是一个主机应用程序的简单示例,它可以加载计算器插件。插件支持不同的计算操作。我们需要创建一个解决方案,它包含6个库项目和一个控制台应用程序。示例应用程序的项目如表36-8所示。这个表列出了需要引用的程序集。在解决方案中引用了其他项目后,还需要把Copy Local属性设置为False,这样程序集就不会复制。但HostApp控制台项目例外,它需要对HostView项目的引用。这个程序集必须复制,才能在主机应用程序中找到。另外,还需要修改所生成的程序集的输出路径,使程序集复制到管道的正确目录下。表 36-8项 目引 用输 出 路 径说 明CalcContractSystem.AddIn.Contract.PipelineContracts这个程序集包含与插件通信的合同。合同用接口定义CalcViewSystem.AddIn .PipelineAddInViewsCalcView程序集包含一个由插件引用的抽象类,这是合同的插件端CalcAddInSystem.AddIn.CalcView.PipelineAddIns CalcAddInCalcAddIn是引用插件视图程序集的插件项目。这个程序集包含插件的实现代码(续表) 项 目引 用输 出 路 径说 明CalcAddInAdapterSystem.AddInSystem.AddIn.ContractCalcViewCalcContract.PipelineAddInSideAdaptersCalcAddInAdapter连接插件视图和合同程序集,把合同映射到插件视图上HostView包含主机视图的抽象类的程序集不需要引用任何插件程序集,也没有解决方案中的其他项目引用HostAdapterSystem.AddInSystem.AddIn.ContractHostViewCalcContract.Pipeline HostSideAdapters主机适配器把主机视图映射到合同上。因此需要引用这些项目HostAppSystem.AddInHostView主机应用程序激活插件36.2.1 计算器合同下面实现合同程序集。合同程序集包含一个合同接口,该接口定义了在主机程序和插件之间通信的协议。下面的代码是为计算器示例应用程序定义的合同。应用程序给合同定义了方法GetOperations()和Operate()。GetOperations()方法返回计算器插件支持的一组数学操作。数学操作是由IOperationContract接口定义的,IOperationContract接口本身就是一个合同,定义了只读属性Name和NumberOperands。Operate()方法调用插件中的操作,它需要IOperation接口定义的一个操作和通过double数组提供的操作数。有了这个接口,插件就支持需要任意多个double操作数的操作,且返回一个double。属性AddInContract由AddInStore用于建立高速缓存。这个属性把类标记为插件合同接口。using System.AddIn.Contract;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsAddInContractpublic interface ICalculatorContract : IContractIListContract GetOperations();double Operate(IOperationContract operation, double operands);public interface IOperationContract : IContractstring Name get; int NumberOperands get; 36.2.2 计算器插件视图插件视图重新定义了插件眼中的合同。该合同定义了接口ICalculatorContract和IOperationContract。为此,插件视图定义了抽象类Calculator和具体的类Operation。在Operation中,没有每个插件都需要的特定实现代码,因为该类已经用插件视图程序集实现了。这个类用属性Name和NumberOperands描述了数学计算的一个操作。抽象类Calculator定义了需要由插件实现的方法。虽然合同定义了需要在应用程序域和进程之间传送的参数和返回类型,但插件视图不需要。这里可以使用类型,以便于插件开发人员编写插件。GetOperations()方法返回IList,而不是IlistOperation,这与合同程序集不同。AddInBase属性把类标识为插件视图,用于存储。using System.AddIn.Pipeline;using System.Collections.Generic;namespace Wrox.ProCSharp.AddInsAddInBasepublic abstract class Calculatorpublic abstract IList GetOperations();public abstract double Operate(Operation operation, double operand);public class Operationpublic string Name get; set; public int NumberOperands get; set; 36.2.3 计算器插件适配器插件适配器把合同映射到插件视图上。这个程序集引用了合同和插件视图程序集。适配器的实现代码需要把合同中的方法IListContract GetOperations()映射到视图方法IList GetOperations()上。该程序集包含类OperationViewToContractAddInAdapter和CalculatorViewToContract- AddInAdapter。这两个类实现了接口IOperationContract和ICalculatorContract。基接口IContract的方法可以通过派生于基类ContractBase来实现。这个基类提供了默认的实现代码。OperationViewToContractAddInAdapter实现了IOperationContract接口的其他成员,并把调用传送给在构造函数中指定的Operation View。类OperationViewToContractAddInAdapter还包含静态帮助方法ViewToContractAdapter()和ContractToViewAdapter(),前者把Operation映射到IOperationContract上,后者把IOperationContract映射到Operation上。using System.AddIn.Pipeline;namespace Wrox.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; public static IOperationContract ViewToContractAdapter(Operation view)return new OperationViewToContractAddInAdapter(view);public static Operation ContractToViewAdapter(IOperationContract contract)return (contract as OperationViewToContractAddInAdapter).view;类CalculatorViewToContractAddInAdapter非常类似于OperationViewToContractAddIn- Adapter:它派生自ContractBase,继承了IContract接口的默认实现代码,还实现了一个合同接口。但ICalculatorContract接口是用GetOperations()和Operate()方法实现的。适配器的Operate()方法调用视图类Calculator的Operate()方法,其中IOperationContract需要转换为Operation。这是使用类OperationViewToContractAddInAdapter的静态帮助方法ContractViewToAdapter()完成的。GetOperations()方法的实现需要把集合IListContract转换为IList。对于这个集合转换,类CollectionAdapters定义了转换方法ToIList()和ToIListContract()。其中ToIListContract()方法用于这个转换。属性AddInAdapter把类标识为插件端适配器,用于插件的存储。using System.AddIn.Contract;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsAddInAdapterinternal class CalculatorViewToContractAddInAdapter : ContractBase,ICalculatorContractprivate Calculator view;public CalculatorViewToContractAddInAdapter(Calculator view)this.view = view;public IListContract GetOperations()return CollectionAdapters.ToIListContract (view.GetOperations(),OperationViewToContractAddInAdapter.ViewToContractAdapter,OperationViewToContractAddInAdapter.ContractToViewAdapter);public double Operate(IOperationContract operation, double operands)return view.Operate(OperationViewToContractAddInAdapter.ContractToViewAdapter(operation), operands);提示:因为适配器类是由.NET反射功能调用的,所以这些类可以使用内部的访问修饰符。这些类是要具体实现的,所以最好使用internal访问修饰符。36.2.4 计算器插件插件现在包含具体的实现代码。它是用类CalculatorV1实现的。插件程序集依赖于插件视图程序集,因为它需要实现抽象类Calculator。属性AddIn把类标记为插件,用于插件的存储,并添加了发布者、版本和描述信息。在主机端,这些信息可以从AddInToken中访问。CalculatorV1在方法GetOperations()中返回一组支持的操作。Operate()方法根据操作计算操作数。using System;using System.AddIn;using System.Collections.Generic;namespace Wrox.ProCSharp.AddInsAddIn(CalculatorAddIn, Publisher=Wrox Press, Version=,Description=Sample AddIn)public class CalculatorV1 : Calculatorprivate List operations;public CalculatorV1()operations = new List ();operations.Add(new Operation() Name = +, NumberOperands = 2 );operations.Add(new Operation() Name = -, NumberOperands = 2 );operations.Add(new Operation() Name = /, NumberOperands = 2 );operations.Add(new Operation() Name = *, NumberOperands = 2 );public override IList GetOperations()return operations;public override double Operate(Operation operation, double operand)switch (operation.Name)case +:return 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 计算器主机视图下面看看主机端的主机视图。与插件视图类似,主机视图也定义了一个抽象类,其方法类似于合同。但是,这里定义的方法是由主机应用程序调用的。类Calculator和Operation都是抽象的,因为其成员由主机适配器实现。它们只需要定义由主机应用程序使用的接口:using System.Collections.Generic;namespace Wrox.ProCSharp.AddInspublic abstract class Calculatorpublic abstract IList GetOperations();public abstract double Operate(Operation operation,params double operand);public abstract class Operationpublic abstract string Name get; public abstract int NumberOperands get; 36.2.6 计算机主机适配器主机适配器程序集引用了主机视图和合同,把视图映射到合同上。类OperationContractToViewHostAdapter实现了抽象类Operation的成员。CalculatorContractTo- ViewHostAdapter实现了抽象类Calculator的成员。在OperationContractToViewHostAdapter中,在构造函数中指定了对合同的引用。适配器类还包含一个ContractHandle实例,它添加了对合同的生存期引用,所以只要主机应用程序需要,插件就保持加载状态。using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsinternal class OperationContractToViewHostAdapter : Operationprivate ContractHandle handle;public IOperationContract Contract 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 OperationHostAdaptersinternal static IOperationContract ViewToContractAdapter(Operation view)return (OperationContractToViewHostAdapter)view).Contract;internal static Operation ContractToViewAdapter(IOperationContract contract)return new OperationContractToViewHostAdapter(contract);类CalculatorContractToViewHostAdapter实现了抽象主机视图类Calculator的成员,并把调用传送给合同。该类还有一个ContractHandle实例,它包含对合同的引用,这类似于插件端类型转换的适配器。但这次仅需要从插件适配器向主机端的类型转换。属性HostAdapter把类标记为一个需要在HostSideAdapters目录下安装的适配器。using System.Collections.Generic;using System.AddIn.Pipeline;namespace Wrox.ProCSharp.AddInsHostAdapterinternal class CalculatorContractToViewHostAdapter : Calculatorprivate ICalculatorContract contract;private ContractHandle handle;public CalculatorContractToViewHostAdapter(ICalculatorContract contract)this.contract = contract;handle = new ContractHandle(contract);public override IList GetOperations()ret
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年数字电路的功能测试项目发展计划
- 正心立德 劳动树人 -小学“新劳动教育”的实践
- 2025年吉林省工业和信息化厅下属事业单位招聘考试笔试试题【答案】
- 2025年务川自治县“特岗计划”招聘 考试笔试试题【答案】
- 2025年南宁市第十三中学招聘高中顶岗教师考试试题【答案】
- 消防员个人工作总结5篇
- 2025年减震系统材料合作协议书
- 2025年低功率气动阀岛用控制阀合作协议书
- 教育新星的成长路径从校园到职场
- 中职数学说课课件教学
- 安保工作月度总结
- 开业美容项目活动方案
- 2025年技术玻璃制品行业市场调研报告
- 2025至2030高纯氯化钾行业产业运行态势及投资规划深度研究报告
- 2025年吉林省中考数学试卷真题(含答案详解)
- 2025年中国自由锻件行业发展运行现状及投资潜力预测报告
- 医学美容技术专业教学标准(高等职业教育专科)2025修订
- QGDW11970.7-2023输变电工程水土保持技术规程第7部分水土保持设施质量检验及评定
- 变电站创优工程汇报
- 党课课件含讲稿:以作风建设新成效激发干事创业新作为
- DB62T 4134-2020 高速公路服务区设计规范
评论
0/150
提交评论