C# 是面向对象的程序设计语言。它提供了 interface 关键字来直接定义接口。同时,System.Reflection 命名空间也提供了访问外部程序集的一系列相关对象。这就为我们在 C# 中实现插件构架打下了坚实的基础。
C#插件构架设计过程
好了,现在我们准备把所有的核心代码都放在 CSPluginKernel 命名空间中。用VSIDE建立一个C#类库工程。在命名空间 CSPluginKernel 中开始我们的代码。
C#插件构架——接口设计
我们的程序编辑器会向插件开放正在编辑的文档对象。程序启动后,就枚举每一个插件并把它连接到主程序,同时传递主程序对象的接口。插件可以通过这个接口来请求主程序对象或访问主程序功能 。
根据上面的需求,我们首先需要一个主程序接口:
- public interface IApplicationObject {
- void Alert( string msg ); // 产生一条信息
- void ShowInStatusBar( string msg ); // 将指定的信息显示在状态栏
- IDocumentObject QueryCurrentDocument(); // 获取当前使用的文档对象
- IDocumentObject[] QueryDocuments(); // 获取所有的文档对象
- // 设置事件处理器
- void SetDelegate( Delegates whichOne , EventHandler targer );
- }
- // 目前只需要这一个事件
- public enum Delegates {
- Delegate_ActiveDocumentChanged ,
- }
然后是 IDocumentObject 接口。插件通过这个接口访问编辑器对象。
- ///
- /// 编辑器对象必须实现这个接口
- ///
- public interface IDocumentObject {
- // 这些属性是 RichTextBox 控件的相应的属性映射
- string SelectionText { get ; set ; }
- Color SelectionColor { get ; set ; }
- Font SelectionFont { get ; set ; }
- int SelectionStart { get ; set ; }
- int SelectionLength { get ; set ; }
- string SelectionRTF { get ; set ; }
- bool HasChanges { get ; }
- void Select( int start , int length );
- void AppendText( string str );
- void SaveFile( string fileName );
- void SaveFile();
- void OpenFile( string fileName );
- void CloseFile();
- }
这个接口不需要过多解释。这里我只实现了RichTextBox控件少数的几个方法,其他可能用得到的,读者自行添加即可。
再然后,根据插件在其生命周期里的行为,设计插件的接口。
- ///
- /// 本程序的插件必须实现这个接口
- ///
- public interface IPlugin {
- ConnectionResult Connect( IApplicationObject app );
- void OnDestory();
- void OnLoad();
- void Run();
- }
- ///
- /// 表示插件与主程序连接的结果
- ///
- public enum ConnectionResult {
- Connection_Success ,
- Connection_Failed
- }
主程序会首先调用 Connect() 方法,并传递 IApplicationObject 给插件。插件在这个过程中做一些初始化工作。然后,插件的 OnLoad() 方法被调用。在这之后,当主程序接收到调用插件的信号时(键盘、鼠标响应)就会调用插件的 Run() 方法来启动这个插件。程序结束时,调用其 OnDestory() 方法。这样,插件的生命才宣告结束。
C#插件构架——加载插件
现在就得用到 System.Refelction 命名空间了。程序在启动时会搜索 plugins 目录下的每一个文件。对于每一个文件,如果它是一个插件,就用 Assembly 对象加载它。然后枚举程序集中的每一个对象。判断一个程序集是否为我们的插件的方法是判断它是否直接或间接实现自 IPlugin。用下面的函数,传递从程序集枚举的对象的System.Type。
- private bool IsValidPlugin( Type t ) {
- bool ret = false ;
- Type[] interfaces = t.GetInterfaces();
- foreach ( Type theInterface in interfaces ) {
- if ( theInterface.FullName == "CSPluginKernel.IPlugin" ) {
- ret = true ;
- break ;
- }
- }
- return ret;
- }
若条件都满足,IsValidPlugin() 就会返回 true 。接着程序就会创建这个对象并把它存于一个 ArrayList 中。
plugins.Add( pluginAssembly.CreateInstance( plugingType.FullName ) );
至此,C#插件构架的设计过程就完成了,现在,你就可以撰写测试代码了。
【编辑推荐】