创建插件框架(2)

** 创建插件框架(2) ** ** **

标题: Create a Plug-In FrameWork

作者: Roy Osherove

译者:easyjoy

出处: MSDN Online ,链接为

http://msdn.microsoft.com/asp.net/archive/default.aspx?pull=/library/en-us/dnaspp/html/pluginframework.asp

摘要:介绍如何为 .Net 应用程序添加插件支持,并提供一个用来框架例子。

代码下载:链接为

http://download.microsoft.com/download/7/2/e/72e64769-58d2-4a91-9686-a1a160bb1bc8/CreatePluginFramework.msi

** 第四步:让应用程序找到新插件 ** ** **

编译好插件后,如何让应用程序知道呢?解决办法很简单:

( 1 )创建应用程序配置文件;

( 2 )在配置文件中创建一项来列出所有插件;

( 3 )创建解析器来解析该项;

要完成( 1 ),只需向主程序添加一个 XML 文件。


** 【 Tip ** ** 】 ** 给该文件取名为 App.Config ,这样,每次编译程序的时候, Visual Studio.Net 会自动把该文件复制到目标文件夹,并改名为 **

  1<yourapp>.Config  ** 。 
  2
  3现在插件编写者可以很容易的往配置文件中添加一下。下面是这个配置文件的样子: 
  4
  5<configuration>
  6<configsections>
  7<section name="plugins" type="Royo.PluggableApp.PluginSectionHandler, PluggableApp"></section>
  8</configsections>
  9<plugins>
 10<plugin type="Royo.Plugins.Custom.EmailPlugin, CustomPlugin"></plugin>
 11</plugins>
 12</configuration>
 13
 14注意到  ** configSections  ** 标签,该标签说明应用程序配置中有一个  plugins  项,而且有一个解析器专门解析  plugins  项。该解析器在类  ** Royo.PluggableApp.PluginSectionHandler  ** 中,装配件的名字是  ** PluggableApp  ** 。下一步中将展示这个类的代码。 
 15
 16接着,配置文件中有  plugins  标签,这列出所有的插件,包括类名和装配件名。 
 17
 18配置文件写好之后,就已经完成一半任务。剩下的是应用程序要读配置信息,并实例化插件。 
 19
 20** 第五步:用  ** ** IConfigurationSectionHandler  ** ** 解析配置文件  ** ** **
 21
 22为了解析出配置文件中的插件信息,框架提供了一个很简单的机制,使得你可以注册一个类作为配置文件中某个特定部分的处理器。对于配置文件中的某个部分,如果框架不能解析,那么就需要一个专门的处理器;否则应用程序会抛出  ConfigurationException  。 
 23
 24为了提供解析  plug-ins  项的类,需要实现下面这个接口,这个接口很简单,代码如下: 
 25
 26** //System.Configuration.IConfigurationSectionHandler  ** , 
 27
 28public interface IConfigurationSectionHandler 
 29
 30{ 
 31
 32public object Create(object parent, object configContext, System.Xml.XmlNode section); 
 33
 34} 
 35
 36我们所要做的就是覆写  Create  方法,在这个方法中解析  XML  节点。在这里,要解析的节点是“  Plugins  ”节点。这个类还需要有一个缺省的构造函数,这样框架可以动态实例化它。类的代码如下: 
 37
 38public class PluginSectionHandler:IConfigurationSectionHandler 
 39
 40{ 
 41
 42public PluginSectionHandler() 
 43
 44{ 
 45
 46} 
 47
 48// Iterate through all the child nodes 
 49
 50//  of the XMLNode that was passed in and create instances 
 51
 52//  of the specified Types by reading the attribite values of the nodes 
 53
 54//  we use a try/Catch here because some of the nodes 
 55
 56//  might contain an invalid reference to a plugin type 
 57
 58public object Create(object parent, 
 59
 60object configContext, 
 61
 62System.Xml.XmlNode section) 
 63
 64{ 
 65
 66PluginCollection plugins = new PluginCollection(); 
 67
 68foreach(XmlNode node in section.ChildNodes) 
 69
 70{ 
 71
 72//Code goes here to instantiate 
 73
 74//and invoke the plugins 
 75
 76. 
 77
 78. 
 79
 80. 
 81
 82} 
 83
 84return plugins; 
 85
 86} 
 87
 88} 
 89
 90如前面提到的,你提供框架需要的信息来处理  configSection  标签中的  plug-ins  项,该项在时间的  plug-ins  标签之前。 
 91
 92<configuration>
 93<configsections>
 94<section name="plugins" type="Royo.PluggableApp.PluginSectionHandler, PluggableApp"></section>
 95</configsections>
 96
 97... 
 98
 99看看上面是如何指定类的。对于的字符串有两部分组成,一个是类的全名(包括名字空间),逗号,所在的装配件。这就是框架用来实例化一个类所需要的信息,也是实例化一个插件所需要的信息。 
100
101** 实例化并调用插件  ** ** **
102
103OK  ,根据下面这段代码,我们该如何实例化插件呢? 
104
105String ClassName = "Royo.Plugins.MyCustomPlugin, MyCustomPlugin" 
106
107IPlugin plugin =  (IPlugin )Activator.CreateInstance(Type.GetType(ClassName)); 
108
109这里发生了哪些事情呢:由于应用程序没有直接引用插件所在的装配件,所以必须要用  ** System.Activator  ** 类。  ** Activator  ** 是一个特殊的类,用来根据任意数量的参数来产生某个对象的实例。如果用过  ASP  或者  Visual Basic  ,也许你记得用过  CreateObject  ()函数,根据某个类的  CLSID  来实例化并返回某个对象。  ** Activator  ** 也是同样的思路,只不过是用不同的参数,返回的是一个  ** System.Object  ** 对象,而不是一个  variant  。 
110
111调用  Activator  的时候,传入的参数是一个类型(  Type  )。用  ** Type.GetType()  ** 方法返回符合插件类型的类型的实例。注意  ** Type.GetType()  ** 以  plug-ins  中的字符串作为参数,这个参数描述了所需要的类和装配件。 
112
113实例化插件后,要强制转换成  IPlugin  。这里需要一个  Try  -  Catch  块,这是为了确保类的确实现了  IPlugin  接口。获取接口实例后就保存下来,然后继续下一个  XML  节点。代码如下: 
114
115public object Create(object parent, 
116
117object configContext, 
118
119System.Xml.XmlNode section) 
120
121{ 
122
123//Derived from CollectionBase 
124
125PluginCollection plugins = new PluginCollection(); 
126
127foreach(XmlNode node in section.ChildNodes) 
128
129{ 
130
131try 
132
133{ 
134
135//Use the Activator class's 'CreateInstance' method 
136
137//to try and create an instance of the plugin by 
138
139//passing in the type name specified in the attribute value 
140
141object plugObject = 
142
143Activator.CreateInstance(Type.GetType(node.Attributes["type"].Value)); 
144
145//Cast this to an IPlugin interface and add to the collection 
146
147IPlugin plugin = (IPlugin)plugObject; 
148
149plugins.Add(plugin); 
150
151} 
152
153catch(Exception e) 
154
155{ 
156
157//Catch any exceptions 
158
159//but continue iterating for more plugins 
160
161} 
162
163} 
164
165return plugins; 
166
167} 
168
169** 调用插件  ** ** **
170
171现在可以调用插件了。别忘了要传入一个  ** IPluginContext  ** 参数。该参数包含插件正常工作所需要的信息。因此需要一个类实现该接口。代码如下: 
172
173public interface IPluginContext 
174
175{ 
176
177string CurrentDocumentText{get;set;} 
178
179} 
180
181public class EditorContext:IPluginContext 
182
183{ 
184
185private string m_CurrentText= string.Empty; 
186
187public EditorContext(string CurrentEditorText) 
188
189{ 
190
191m_CurrentText = CurrentEditorText; 
192
193} 
194
195public string CurrentDocumentText 
196
197{ 
198
199get{return m_CurrentText;} 
200
201set{m_CurrentText = value;} 
202
203} 
204
205} 
206
207下面就可以调用插件了: 
208
209private void ExecutePlugin(IPlugin plugin) 
210
211{ 
212
213//create a context object to pass to the plugin 
214
215EditorContext context = new EditorContext(txtText.Text); 
216
217//The plugin Changes the Text property of the context 
218
219plugin.PerformAction(context); 
220
221txtText.Text= context.CurrentDocumentText; 
222
223} 
224
225} 
226
227** 总结  ** ** **
228
229(略) 
230
231【完】</configuration></yourapp>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus