** 创建插件框架(2) ** ** **
标题: Create a Plug-In FrameWork
作者: Roy Osherove
译者:easyjoy
出处: MSDN Online ,链接为
摘要:介绍如何为 .Net 应用程序添加插件支持,并提供一个用来框架例子。
代码下载:链接为
** 第四步:让应用程序找到新插件 ** ** **
编译好插件后,如何让应用程序知道呢?解决办法很简单:
( 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>