1、主程序:
核心程序,把变动不大项目必须的部分写入主程序。
以客户管理为例:
在客户管理中客户基本档案管理、客户分类管理可以看做程序固定不变的部分也是程序初期必须实现的功能,作为主程序部分优先开发,客户简历,客户兴趣爱好,客户调查这些相对变化比较大,可以作为后期扩展开发,写成插件。
我们在这里只说插件开发实现部分,其它的不在详述。
新建一个解决方案:插件式开发客户管理
在解决方案里新建windows应用程序项目:CRM
到这里先放下,进入插件实现部分。
2、接口定义:
在解决方案里新建一个类库项目:CRM.IPlugin
在项目里新建一个类:IVote.cs
代码:
using System;
namespace CRM.IPlugin
{
public interface IVote
{
void Setup();
string GetPluginName();
}
}
3:、插件编写:
在解决方案里新建一个类库项目:CRM.Plugins
添加引用:
.NET: System.Windows.Forms
项目: CRM.IPlugin
在项目里新建一个窗体:Vote.cs
窗体代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace CRM.Plugins
{
public partial class Vote : Form,CRM.IPlugin.IVote
{
public Vote()
{
InitializeComponent();
}
private void Vote_Load(object sender, EventArgs e)
{
this.Text = "客户投票调查";
}
#region IVote 成员
public void Setup()
{
this.Show();
}
public string GetPluginName()
{
return "客户投票调查";
}
#endregion
}
}
到此,插件算是编写完成。下面找到CRM项目文件夹在bin\debug目录下新建目录Plugins,回到vs解决方案资源管理器中选择CRM.Plugins项目,右键-》属性-》生成-》输出目录设置为..\CRM\bin\Debug\Plugins\,最后再生成一下CRM.Plugins项目。
4、调用插件:
回到项目CRM,打开窗体在窗体上拖放一个ListBox控件,切换到代码视图输入如下的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace CRM
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.LoadPlugins();
}
//存储插件DLL文件的路径
private System.Collections.ArrayList PluginsDLL = new System.Collections.ArrayList();
/// <summary>
/// 加载所有插件
/// </summary>
private void LoadPlugins()
{
try
{
//获取Plugins目录文件
string[] PluginFiles = System.IO.Directory.GetFiles(Application.StartupPath + @"\Plugins");
foreach (string PluginFile in PluginFiles)
{
//dll文件才是有效的插件程序集
if (PluginFile.ToUpper().EndsWith(".DLL"))
{
//通过反射加载dll程序集
System.Reflection.Assembly Ab = System.Reflection.Assembly.LoadFrom(PluginFile);
//获取加载的dll程序集里面的类名称
Type[] Types = Ab.GetTypes();
foreach (Type T in Types)
{
//检查类是否实现了定义的IVote接口
if (T.GetInterface("IVote") != null)
{
listBox1.Items.Add(T.FullName);
PluginsDLL.Add(PluginFile);
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// 调用插件方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox1_Click(object sender, EventArgs e)
{
try
{
if (listBox1.SelectedIndex == -1)
{
return;
}
string PluginDLLFile = PluginsDLL[listBox1.SelectedIndex].ToString();
//加载插件DLL程序集
System.Reflection.Assembly Ab = System.Reflection.Assembly.LoadFrom(PluginDLLFile);
//创建插件中名称为listBox1中选中项名称的类的实例
object Plugin = Ab.CreateInstance(listBox1.SelectedItem.ToString());
Type T = Plugin.GetType();
//获取类中的方法GetPluginName
System.Reflection.MethodInfo PluginMethodReturnValue = T.GetMethod("GetPluginName");
//执行有返回值的方法
object returnValue = PluginMethodReturnValue.Invoke(Plugin, null);
MessageBox.Show("您调用的插件是:"+returnValue.ToString());
//获取类中的方法Setup
System.Reflection.MethodInfo PluginMethod = T.GetMethod("Setup");
//执行无返回值的方法
PluginMethod.Invoke(Plugin, null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
5、总结:
插件式编程是牺牲了程序的一部分执行效率来换得开发效率,我也只是初步理解。
对于这个示例程序来说,我觉得最大的缺陷有以下几点:
1、调用插件方法的时候每次都要加载插件DLL文件和创建相应的实例,这里还有很大的性能改进空间,应该可以用缓存来存储创建的实例,但是由于对winform下的缓存我还没有掌握,暂时没有解决。
2、每次调用都会打开一个窗口,我想到一个解决办法就是在插件窗体调用方法Setup里检测窗体是否在applicaton.openForm里面,在的话就把创建的对象释放掉,不在则显示,觉得不是那么好,希望大家给出更好的办法。
代码如下,修改CRM.Plugins里面的vote窗体的Setup方法:
public void Setup()
{
System.Windows.Forms.FormCollection FormColl = Application.OpenForms;
if (FormColl[this.Name] != null)
{
(FormColl[this.Name] as Form).WindowState = FormWindowState.Normal;
(FormColl[this.Name] as Form).Focus();
this.Dispose();
}
else
{
this.Show();
}
}