C# 插件构架实战

C#实现插件框架--过程 以下例子实现界面和数据操作的分离,界面成为一个dll,数据操作部分成为一个dll。 先上一个框图:     从上面的框图可以看出整体上需要:三个接口 下面是代码: (1)建立第一个项目(以便形成dll),名称为:PluginKernel,此项目里面只需定义三个接口,为了层次分明,这三个接口分在两个.cs文件中。 先在IControl.cs(或IApp)文件中定义两个接口: na 阅读详情
一、引言
     1. 问题的引入
    假设你设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话——他们要求增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求——你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,却不能保证用户的需求不会再次变更。也就是说, 需求蔓延的可能性依然存在。因此,这种情况下插件构架更能显示出它的优越性。
     2. 几个解决方案的对比
    我总结了一下我所接触到的插件构架,大致上可分为以下几类:
i> 脚本式
    使用某种语言把插件的程序逻辑写成脚本代码。而这种语言可以是 Python ,或是其他现存的已经经过用户长时间考验的脚本语言。甚至,你可以自行设计一种脚本语言来配合你程序的特殊需要。当然,用当今最流行的 XML 是再合适不过了。
    这种形式的特点在于,稍有点编程知识的用户就可以自行修改你的脚本( ^_^ 假如你不加密它的话)。我们无法论证这是好处还是坏处。因为,这种情况所造成的后果是不可预知的。
ii> 动态函数库 DLL
    插件功能以动态库函数的形式存在。主程序通过某种渠道(插件编写者或某些工具)获得插件 DLL 中的函数签名,然后在合适的地方调用它们。用过 Matlab 的读者都知道, Matlab 中的各项功能几乎都是些动态链入的函数。
iii> 聚合式
    顾名思义,就是把插件功能直接写成 EXE 。主程序除了完成自己的职责外,还负责调度这些“插件”。我不喜欢这种形式。这使插件与插件之间,主程序与插件之间(主要是这一点)的信息交流困难了许多。巴比伦塔的失败 [1] 从某种程度上讲就是 信息交流无法实现造成的。
iv> COM 组件
    COM [2] 的产生给这个世界增添了几分活力。只有 接口!我们的插件需要做的只是实现程序定义的接口。主程序不需要知道插件怎样实现预定的功能,它只需要通过接口访问插件,并提供主程序相关对象的接口。这样一来,主程序与各插件之间的信息交流就变得异常简单。并且,插件对于主程序来说是完全透明的。
     3. 决策
    C# 是面向对象的程序设计语言。它提供了 interface 关键字来直接定义接口。同时, System.Reflection 命名空间也提供了访问外部程序集的一系列相关对象。这就为我们在 C# 中实现插件构架打下了坚实的基础。
    下面,我们将以一个具有插件构架的程序编辑器为例,来阐述这种构架在 C# 中的实现。
二、设计过程
    好了,现在我们准备把所有的核心代码都放在 CSPluginKernel 命名空间中。用VSIDE建立一个C#类库工程。在命名空间 CSPluginKernel 中开始我们的代码。
     1. 接口设计
    我们的程序编辑器会向插件开放正在编辑的 文档对象。程序启动后,就枚举每一个插件并把它 连接到主程序,同时传递 主程序对象的接口。插件可以通过这个接口来 请求主程序 对象或访问主程序功能 。
    根据上面的需求,我们首先需要一个主程序接口:
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() 方法。这样,插件的生命才宣告结束。
     2. 插件信息的存储与获取
    一个插件需要有它的名称 、版本等信息。作为设计者的你,也一定要留下你的尊姓大名和个人网站等用来宣传自己。 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 loadWhenStart ) {
          // 细节已略去
     }
     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 LoadWhenStart { get { return _LoadWhenStart; } }
     ///
     /// 用来存储一些有用的信息
     ///
     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 = 0;
     // 暂时不会用
     private bool _LoadWhenStart = true ;
}
     用这个类修饰你的插件,并让他实现 IPlugin 接口:
///
/// My Pluging 1( Just for test )
///
[
     PluginInfo(
          "My Pluging 1( Just for test )" ,
          "1.0" ,
          "Jack H Hansen" ,
          "http://blog.youkuaiyun.com/matrix2003b" , true )
]
public class MyPlugin1 : IPlugin {
     public MyPlugin1() { }
 
     #region IPlugin 成员
     // 细节已略去
     #endregion
 
     private IApplicationObject _App;
     private IDocumentObject _CurDoc;
}
     3. 加载插件
     现在就得用到 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 ) );
     现在,你就可以撰写测试代码了。
三、源代码
     由于篇幅所限,完整的源代码(包含测试用例)请在下面的链接下载。下载后请用 VS.NET2003 打开,重新生成解决方案即可(需要 .NET Framework 1.1)。测试用例是一个在 RichTextBox 控件里插入红色文本的插件。很简单,只作测试之用。
四、结语
     That's all! 有了这种插件构架,可怜的程序员们就再也不用为需求蔓延耗费心机了。另外,欢迎对本文以及本文的附加代码作出评价。还有,就是,常去 我的 Blog 看看~~ ^_^
注:
[1] 巴比伦塔的失败    《人月神话》, Frederick P. Brooks Jr.  第 7 章 为什么巴比伦塔会失败
[2] COM    有关 COM/COM+ 的详细技术细节请参见《 Mastering COM and COM+ 》 , Ash Rofail , Yasser Shohoud.
 
在C#程序中实现插件架构 在C#程序中实现插件架构 - Sunmast翻译原文链接:http://www.cuj.com/documents/s=8209/cujweb0301walcheske/原文作者:Shawn Patrick Walcheske译者:电子科技大学 夏桅[引言]在.NET框架下的C#语言,和其他.NET语言一样提供了很多强大的特性和机制.其中一些是全新的,而有些则是从以前的语言和平 阅读详情

相关推荐

C#实现插件式架构

1.定义插件接口,将其编译为DLLnamespace PluginInterface{    public interface IShow    {        string show();    }}2 .编写插件,引用上面的DLL,实现上面定义的接口,也编译为DLL//插件Anamespace PluginInterface{    public clas

emailqjc的专栏 5838

c#下的插件式开发

动态加载符合条件的Dll,动态调用Dll的方法以及参数。

weixin_48466684的博客 4626

VS C# 代码折叠(大纲) 插件

VS C# 代码折叠(大纲) 插件

C#插件构架实战 + Visual C#插件构架实战补遗

C#插件构架实战 C# 插件构架实战 Jack H Hansen [ 2004-07-27 ] Keywords C# 插件 反射(System.Reflection) 属性(System.Attribute) 一、引言 1. 问题的引入 假 设你设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话——他们要求增加新的功能。确定了用户的需求后,你竟然...

bangzhiqian8115的博客 135

C#插件式框架开发完整Demo

C#插件式框架开发完整DemoC#插件式框架开发完整DemoC#插件式框架开发完整DemoC#插件式框架开发完整DemoC#插件式框架开发完整DemoC#插件式框架开发完整Demo

别再傻傻分不清!5分钟搞懂倾斜摄影里‘地面分辨率’和‘模型精度’到底啥关系

本文深入解析倾斜摄影中‘地面分辨率’与‘模型精度’的关系,帮助读者快速理解两者的本质区别与内在关联。通过实战案例和公式计算,详细说明如何从航飞设计到精度验证全流程控制,避免常见认知误区,并针对不同应用场景提供精度需求建议。

weixin_33700350的博客 476

C#插件构架实战

C#插件构架实战

蓝月的专栏 926

【LPDDR4 - 学习二】LPDDR4 介绍 ——LPDDR4 芯片引脚说明解析

本文系统解析了LPDDR4内存芯片的引脚定义与特性,重点介绍了272球POPFBGA封装的双通道架构。内容涵盖电源管理(三级供电架构与上电时序)、控制信号(差分时钟与复用命令总线)、数据接口(16位双向数据总线与选通信号)以及校准复位等核心模块。通过表格详细列明了各引脚电压范围、功能定义及工程注意事项,并附有典型封装球位分布图。全文基于JEDEC标准,为移动设备内存设计提供全面的硬件参考,特别强调电源时序控制、信号完整性及抗干扰设计等关键点。

zap15622077817的博客 2637

Visual C#插件构架实战补遗

当别人在用户快速增长的需求中忙得不可开交时(用户总是不能在第一次需求分析时将需求完完整整的告诉你),而你,你的团队只需要作一点工作就可以让“贪得无厌”的用户得到满足,从而提高了效率,让团队有更多的的时间来创造,而不是去做无谓的修改。任何事物都有它规范的一面,我们希望我们的团队出品的部署在同一平台的所有应用都有相同的框架,相同的部署形式。在软件开发的过程中,设计的过程往往比写代码的过程要难得多。对于网友 jan 的指教,我大概明了,但人的思维差别太大,我不敢保证我的理解是完全符合 jan 的意思的。

biyusr的专栏 524

别光看手册!手把手教你用SGM2036 LDO设计一个稳定3.3V转1.8V的MCU供电电路(附PCB布局避坑指南)

本文详细介绍了如何使用SGM2036 LDO设计一个稳定的3.3V转1.8V的MCU供电电路,包括需求分析、芯片选型、外围器件计算、PCB布局避坑指南及实测验证。通过实际案例和计算示例,帮助工程师快速掌握LDO应用技巧,确保系统电源稳定可靠。

weixin_42548893的博客 505

从理论到代码:手把手教你用Matlab复现MMC的HSS阻抗模型(附避坑指南)

本文详细介绍了如何使用Matlab复现MMC的HSS阻抗模型,涵盖谐波状态空间(HSS)理论、代码实现步骤及常见问题解决方案。通过谐波线性化和序阻抗建模技术,帮助工程师准确分析MMC的阻抗特性,并提供避坑指南以解决矩阵维度错误、收敛性问题和频域转换陷阱等难点。

weixin_42561040的博客 357

C# 插件构架实战(转帖)

一、引言    1. 问题的引入    假设你设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话——他们要求增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求——你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,却不能保证用户的需求不会再次变更。也就是说,需求蔓延的可能性依然存在。因此,这种情

雨雪之家 927

Zlib1.2.11源码编译(Win10+VS2019)

下载 下载网址: zlib Home Site 得到文件: 解压缩到“D:\SourceCodes\zlib-1.2.11” 使用CMake配置、生成并打开项目 将CMakeLists.txt拖入CMake,配置(Configure): 生成(Generate) 打开项目(Open Project),CMake调用VS2019打开项目 生成debug版本 构建项目ALL_BUILD。成功生成debug版本 在“D:\Sourc...

Alexabc3000的专栏 1892

Visual C# 插件构架实战

一、引言 1. 问题的引入 假设你设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话——他们要求增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求——你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,却不能保证用户的需求不会再次变更。也就是说,需求蔓延的可能性依然存在。因此,这种情况下插件构架

The key to .Net 400

WorkBuddy KiCad 实战:国产 AI 搭子不只会写代码,传感器板从零走到可审查

真正可用的 PCB 教程不能承诺“一键打样”。本课把 WorkBuddy 放在需求拆分、文件生成和审查记录的位置,KiCad 负责 ERC、DRC 与制造文件,工程师负责最终判断。 本课不会用“AI 一键完成”制造错觉,而是把 WorkBuddy、KiCad 8.0、Python 3.11 与人工审阅组成一条可复现链路。你会从“已确认的接口表、器件库来源、板框与电源方案”出发,得到“KiCad 工程、原理图、初始 BOM、ERC 报告与审查记录”,并学会用真实命令、错误信号和停止条件判断成果能不能进入下一阶

yueyeguzhuo的专栏 210

Python爬虫教程:抓取Android或iOS应用中的数据

随着智能手机的普及,移动应用程序已经成为我们生活中不可或缺的一部分。从社交媒体、在线购物到金融服务和健康管理,几乎所有领域都有相应的手机应用程序(App)服务用户。而这些应用程序通常会存储大量的数据,例如用户行为、产品信息、订单记录等。如果我们能有效地抓取这些数据,可以为数据分析、商业智能、用户行为分析等提供有力支持。本文将重点介绍如何使用Python爬虫技术抓取Android或iOS应用中的数据,尤其是如何从这些应用的后端接口、存储、或网页中提取数据。我们将探讨如何通过反向工程技术访问移动应用中的API接

2201_76125261的博客 801
上一篇: 工作流理论
下一篇: SPS2007中的工作流
SmartJavaer
博客等级 码龄21年 13粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值