C# 模块化开发技术讨论 你自己都说了,用反射呀。定义好接口,通过反射满足接口类型的DLL可以实现让系统支持插件的功能 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 对于一般人,不要胡乱空洞地说什么时髦的“反射”概念。你能够将“接口设计和编程”使用上一千次,对反射也未必需要使用上一次。软件开发并没有什么是非黑白,很多时候“过分”甚至不如“不及”。如果明明可以正常地、高效、在运行之前就能检查出编码bug地使用接口的地方,你偏要反射,那么这就是问题。许多时候,“过分”就是一种问题。不是说堆砌技术很多、很时髦就一定显得好。没有经验的人才会以为堆砌技术越多越好。 interface + xml + 反射定义好通用的接口,二次开发的用户用xml配置(要事先定义好格式) 就是说多用接口来做,我现在的想法是系统的菜单或者别的模块或功能调用DLL来做,等不用或者更改就换别的DLL,因为我想做的程序要对以后的二次开发或者添加模块有较好的支持。我知道从哪里入手,以前一直做小项目,没有接触过这方面的东西和经验。 定义接口比如public interface Ixxx{ ... ...} 然后在程序中相应的地方使用这个接口,至于接口实例可以如下实现,这样可以让系统支持插件,便于二次开发和功能扩展。实际的应用中如果提供二次开发比如像word那样可以在主程序中挂接二次开发的菜单,那么你可以在接口中定义这些菜单和事件,下面的代码包含了如何将一个dll文件动态的转换为实例,这些代码比较简单,实际应用中可能相对会复杂一些,这要看你需要实现的功能,希望对你有帮助。这儿读取你所有的二次开发插件,插件后缀名你自己定,实际上就是dll文件string Files = System.IO.Directory.GetFiles(Path + "\\addin", "*.pug"); foreach (string file in Files) { 这儿根据文件反射为程序集 Assembly a = System.Reflection.Assembly.LoadFile(file); if (a != null) { Type[] Types = a.GetTypes(); for (int i = 0; i < Types.Length ; i++) { 查找包含Ixxx接口的type,如果有命名空间则查询的字符串包含命名空间比如:zzz.Ixxx Type t= Types [i].GetInterface("Ixxx"); 这儿的obj就是你的Ixxx接口对象实例。 Ixxx obj = (Ixxx)Activator.CreateInstance(Type); } } } 所谓模块化设计的原理,就是将内部的实现细节封装,只通过一个定义好的接口与外部通讯。作为模块,也不一定要求它是主动的,一样也有被动。因此将COM组件作为一个模块是没有问题的。就面向对象观点来看,一个对象的动作是通过各类消息触发的,有一些对象的动作是被它内部的消息触发(比如这个对象内部有线程),可以说它是主动的,有一些对象的动作只被外部的消息触发。主动的对象常常向其他的对象发送消息,以触发其他对象的动作。 对于中心控制器,可以这样理解,模块是有层次化的,在同一层次而言,模块与它同级别的其他模块之间是相互独立的,但对于上一级别而言,这些模块同属于上一级的一个大模块,大的模块从内部实现的观点看,这些组件之间一定是有关系的,可以用某些组件控制其他组件的运行,同时上一级模块对下一级模块有依赖关系。例如:CPU和内存都是插在主板上的模块,彼此是独立的,你换一个CPU对内存没有影响,但从整个计算机来看,是用CPU控制了内存的存取,就是说从计算机这一模块层次来说,把CPU作为它内部实现种的中央控制器,由它来触发其它组件的动作。 我最近想出的办法是把反射要用的类名什么的直接追加到DLL文件后面,反射前先从DLL文件末尾读取,这样就不需要额外的XML等配置文件了,建议你也试试. 我现在做了一个项目 是这样的思路看看对不对,请大侠多多指教主程序----------------项目sms反射类----------------sms_aXXX(不知道改叫什么)----sms_b数据库操作类-----------sms_d关于sms_b的接口类------sms_I数据模型类-------------sms_m一些公用的方法类--------sms_fnamespace GT_SMS{...this.dataGridView1.DataSource = B_Log.G_Log_SelectAll();...}namespace SMS_B{... /// <summary> /// 创建数据访问接口 /// </summary> private static readonly I_Log I = Interface_Assembly.i_log(); /// <summary> /// 日志所有信息 /// </summary> /// <returns></returns> public static DataTable G_Log_SelectAll() { return I.G_Log_SelectAll(); }...}namespace SMS_A{... /// <summary> /// 反射错误类 /// </summary> /// <returns></returns> public static I_Error i_error() { return (I_Error)Assembly.Load("SMS_D").CreateInstance("SMS_D.D_Error"); } /// <summary> /// 反射日志类 /// </summary> /// <returns></returns> public static I_Log i_log() { return (I_Log)Assembly.Load("SMS_D").CreateInstance("SMS_D.D_Log"); }...}namespace SMS_I{... /// <summary> /// 查询所有信息 /// </summary> /// <returns></returns> DataTable G_Interface_SelectALL();...}namespace SMS_D{ /// <summary> /// 日志执行类 /// </summary> public class D_Log : I_Log { /// <summary> /// 日志所有信息 /// </summary> /// <returns></returns> public DataTable G_Log_SelectAll() { return SqlHelper.ExecuteDataset(GT_Function.StrCon, "G_Log_SelectAll", null).Tables[0]; }...}namespace SMS_M{ /// <summary> /// 日志类 /// </summary> public class M_Log { private int _LId; private string _Log; private DateTime _Date; /// <summary> /// 日志时间 /// </summary> public DateTime Date { get { return _Date; } set { _Date = value; } } /// <summary> /// 日志内容 /// </summary> public string Log { get { return _Log; } set { _Log = value; } }...} 感觉使用MEF甚至是MAF这类比较成熟的解决方案都要比自己做反射要好很多。 MEF/MAF我不懂 有没有资料给个链接 MEF用的怎么样 有用的实际项目的没 说说 MEF吧,.NET4.0自带了。。搜索Managed Extensibility Framework。。或者学习下SharpDevelop的AddInTree插件树原理,用它的core.dll来做。。 是啊 不是有MEF么 可以试试的 或者看下这个插件开发代码 请问如何实现调用另一个类中实现的接口 WPF webbrowser怎样才能在其中一个网页点击某个内容时不要在IE打开,而在当前控件内打开…… Graphics 画线清楚 欢迎讨论,想弄个提供 [影视] [软件] [音乐]下载的服务器 不知道行不行? winform下的WebRequest怎么发送gb2312编码的请求? 急,请高手指点(算法问题) 如何让c#忽略存储过程中的异常 如何生成 .dll文件?? 一个控件能有两个DataSouce?? 等级考试错报三级数据库,悔呀,散分....... DrowdownList下拉框选择省市问题 dataGridView控件如何判断已选择记录
对于一般人,不要胡乱空洞地说什么时髦的“反射”概念。你能够将“接口设计和编程”使用上一千次,对反射也未必需要使用上一次。软件开发并没有什么是非黑白,很多时候“过分”甚至不如“不及”。如果明明可以正常地、高效、在运行之前就能检查出编码bug地使用接口的地方,你偏要反射,那么这就是问题。许多时候,“过分”就是一种问题。不是说堆砌技术很多、很时髦就一定显得好。没有经验的人才会以为堆砌技术越多越好。
定义好通用的接口,二次开发的用户用xml配置(要事先定义好格式)
public interface Ixxx
{
...
...
} 然后在程序中相应的地方使用这个接口,至于接口实例可以如下实现,这样可以让系统支持插件,便于二次开发和功能扩展。实际的应用中如果提供二次开发比如像word那样可以在主程序中挂接二次开发的菜单,那么你可以在接口中定义这些菜单和事件,下面的代码包含了如何将一个dll文件动态的转换为实例,这些代码比较简单,实际应用中可能相对会复杂一些,这要看你需要实现的功能,希望对你有帮助。这儿读取你所有的二次开发插件,插件后缀名你自己定,实际上就是dll文件
string Files = System.IO.Directory.GetFiles(Path + "\\addin", "*.pug"); foreach (string file in Files)
{
这儿根据文件反射为程序集
Assembly a = System.Reflection.Assembly.LoadFile(file);
if (a != null)
{
Type[] Types = a.GetTypes();
for (int i = 0; i < Types.Length ; i++)
{ 查找包含Ixxx接口的type,如果有命名空间则查询的字符串包含命名空间比如:zzz.Ixxx
Type t= Types [i].GetInterface("Ixxx");
这儿的obj就是你的Ixxx接口对象实例。
Ixxx obj = (Ixxx)Activator.CreateInstance(Type);
}
}
}
对于中心控制器,可以这样理解,模块是有层次化的,在同一层次而言,模块与它同级别的其他模块之间是相互独立的,但对于上一级别而言,这些模块同属于上一级的一个大模块,大的模块从内部实现的观点看,这些组件之间一定是有关系的,可以用某些组件控制其他组件的运行,同时上一级模块对下一级模块有依赖关系。例如:CPU和内存都是插在主板上的模块,彼此是独立的,你换一个CPU对内存没有影响,但从整个计算机来看,是用CPU控制了内存的存取,就是说从计算机这一模块层次来说,把CPU作为它内部实现种的中央控制器,由它来触发其它组件的动作。
我最近想出的办法是把反射要用的类名什么的直接追加到DLL文件后面,反射前先从DLL文件末尾读取,这样就不需要额外的XML等配置文件了,建议你也试试.
主程序----------------项目sms
反射类----------------sms_a
XXX(不知道改叫什么)----sms_b
数据库操作类-----------sms_d
关于sms_b的接口类------sms_I
数据模型类-------------sms_m
一些公用的方法类--------sms_fnamespace GT_SMS
{
...
this.dataGridView1.DataSource = B_Log.G_Log_SelectAll();
...
}namespace SMS_B
{
...
/// <summary>
/// 创建数据访问接口
/// </summary>
private static readonly I_Log I = Interface_Assembly.i_log();
/// <summary>
/// 日志所有信息
/// </summary>
/// <returns></returns>
public static DataTable G_Log_SelectAll()
{
return I.G_Log_SelectAll();
}
...
}namespace SMS_A
{
...
/// <summary>
/// 反射错误类
/// </summary>
/// <returns></returns>
public static I_Error i_error()
{
return (I_Error)Assembly.Load("SMS_D").CreateInstance("SMS_D.D_Error");
}
/// <summary>
/// 反射日志类
/// </summary>
/// <returns></returns>
public static I_Log i_log()
{
return (I_Log)Assembly.Load("SMS_D").CreateInstance("SMS_D.D_Log");
}
...
}namespace SMS_I
{
...
/// <summary>
/// 查询所有信息
/// </summary>
/// <returns></returns>
DataTable G_Interface_SelectALL();
...
}namespace SMS_D
{
/// <summary>
/// 日志执行类
/// </summary>
public class D_Log : I_Log
{
/// <summary>
/// 日志所有信息
/// </summary>
/// <returns></returns>
public DataTable G_Log_SelectAll()
{
return SqlHelper.ExecuteDataset(GT_Function.StrCon, "G_Log_SelectAll", null).Tables[0];
}
...
}namespace SMS_M
{
/// <summary>
/// 日志类
/// </summary>
public class M_Log
{
private int _LId;
private string _Log;
private DateTime _Date;
/// <summary>
/// 日志时间
/// </summary>
public DateTime Date
{
get { return _Date; }
set { _Date = value; }
}
/// <summary>
/// 日志内容
/// </summary>
public string Log
{
get { return _Log; }
set { _Log = value; }
}
...
}
或者看下这个
插件开发代码