知识大全 Visual C# 插件构架实战
Posted 知
篇首语:成功的人是跟别人学习经验,失败的人只跟自己学习经验。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Visual C# 插件构架实战相关的知识,希望对你有一定的参考价值。
Visual C# 插件构架实战 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
一 引言
问题的引入
假设你设计的程序已经部署到用户的计算机上 并且能够正常运行了 但是有一天 用户打来了电话——他们要求增加新的功能 确定了用户的需求后 你竟然发现原有的软件架构已经无法胜任新增任务的需求——你需要重新设计这个应用了!但问题是 就算你又用了一个开发周期完成了用户需要的应用 却不能保证用户的需求不会再次变更 也就是说 需求蔓延的可能性依然存在 因此 这种情况下插件构架更能显示出它的优越性
几个解决方案的对比
我总结了一下我所接触到的插件构架 大致上可分为以下几类
i> 脚本式
使用某种语言把插件的程序逻辑写成脚本代码 而这种语言可以是 Python 或是其他现存的已经经过用户长时间考验的脚本语言 甚至 你可以自行设计一种脚本语言来配合你程序的特殊需要 当然 用当今最流行的 XML 是再合适不过了
这种形式的特点在于 稍有点编程知识的用户就可以自行修改你的脚本( ^_^ 假如你不加密它的话) 我们无法论证这是好处还是坏处 因为 这种情况所造成的后果是不可预知的
ii> 动态函数库 DLL
插件功能以动态库函数的形式存在 主程序通过某种渠道(插件编写者或某些工具)获得插件 DLL 中的函数签名 然后在合适的地方调用它们 用过 Matlab 的读者都知道 Matlab 中的各项功能几乎都是些动态链入的函数
iii> 聚合式
顾名思义 就是把插件功能直接写成 EXE 主程序除了完成自己的职责外 还负责调度这些 插件 我不喜欢这种形式 这使插件与插件之间 主程序与插件之间(主要是这一点)的信息交流困难了许多 巴比伦塔的失败 [ ] 从某种程度上讲就是信息交流无法实现造成的
iv> 组件
[ ] 的产生给这个世界增添了几分活力 只有接口!我们的插件需要做的只是实现程序定义的接口 主程序不需要知道插件怎样实现预定的功能 它只需要通过接口访问插件 并提供主程序相关对象的接口 这样一来 主程序与各插件之间的信息交流就变得异常简单 并且 插件对于主程序来说是完全透明的
决策
C# 是面向对象的程序设计语言 它提供了 interface 关键字来直接定义接口 同时 System Reflection 命名空间也提供了访问外部程序集的一系列相关对象 这就为我们在 C# 中实现插件构架打下了坚实的基础
下面 我们将以一个具有插件构架的程序编辑器为例 来阐述这种构架在 C# 中的实现
二 设计过程
好了 现在我们准备把所有的核心代码都放在 CSPluginKernel 命名空间中 用VSIDE建立一个C#类库工程 在命名空间 CSPluginKernel 中开始我们的代码
接口设计
我们的程序编辑器会向插件开放正在编辑的文档对象 程序启动后 就枚举每一个插件并把它连接到主程序 同时传递主程序对象的接口 插件可以通过这个接口来请求主程序对象或访问主程序功能
根据上面的需求 我们首先需要一个主程序接口
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 Selection
int SelectionLength get ; set ;
string SelectionRTF get ; set ;
bool HasChanges get ;
void Select( int
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 Attribute 继承来的类 PluginInfoArrtibute
///
/// 用来指定一个插件的相关信息
///
public class PluginInfoAttribute : System Attribute
///
/// Deprecated Do not use
///
public PluginInfoAttribute()
public PluginInfoAttribute(
string name string version
string author string webpage bool loadWhen
// 细节已略去
public string Name get return _Name;
public string Version get return _Version;
public string Author get return _Author;
public string Webpage get return _Webpage;
public bool LoadWhen
///
/// 用来存储一些有用的信息
///
public object Tag
get return _Tag;
set _Tag = value ;
///
/// 用来存储序号
///
public int Index
get return _Index;
set _Index = value ;
private string _Name = ;
private string _Version = ;
private string _Author = ;
private string _Webpage = ;
private object _Tag = null ;
private int _Index = ;
// 暂时不会用
private bool _LoadWhen
用这个类修饰你的插件 并让他实现 IPlugin 接口
///
/// My Pluging ( Just for test )
///
[
PluginInfo( My Pluging ( Just for test ) Jack H Hansen
true )
]
public class MyPlugin : IPlugin
public MyPlugin ()
#region IPlugin 成员
// 细节已略去
#endregion
private IApplicationObject _App;
private IDocumentObject _CurDoc;
加载插件
现在就得用到 System Refelction 命名空间了 程序在启动时会搜索 plugins 目录下的每一个文件 对于每一个文件 如果它是一个插件 就用 Assembly 对象加载它 然后枚举程序集中的每一个对象 判断一个程序集是否为我们的插件的方法是判断它是否直接或间接实现自 IPlugin 用下面的函数 传递从程序集枚举的对象的System Type
private bool IsValidPlugin( Type t )
bool ret = false ;
Type[] interfaces = t GetInterfaces();
fo
if ( theInterface FullName == CSPluginKernel IPlugin )
ret = true ;
break ;
return ret;
若条件都满足 IsValidPlugin() 就会返回 true 接着程序就会创建这个对象并把它存于一个 ArrayList 中
plugins Add( pluginAssembly CreateInstance( plugingType FullName ) );
相关参考
VisualC#接口转换 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! C#中不仅支持Net平台
知识大全 Visual C#或Visual Basic创建Web应用程序(上)
VisualC#或VisualBasic创建Web应用程序(上) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起
知识大全 Visual C#或Visual Basic创建Web应用程序(下)
VisualC#或VisualBasic创建Web应用程序(下) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起
eclipse3.2添加C#插件 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nbs
VisualC#访问接口 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 对接口成员的访问 &n
VisualC#接口慨述 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 接口(interface
VisualC#打造“浏览器” 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! VisualC#是
VisualC#编程入门之语句 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 程序的活动是通过语
初入门道:VisualC#常用快捷键 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &n
用VisualC#动态生成组件 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 通常在写程序的时候