【讨论】从吉日的一段话说起+寻找WinForm架构的最佳实践

这两天园子里最火的莫过于吉日的白话反射,导致包子的批判,然后引来了老赵的两篇文章,然后又有若干人等一堆反射技术文章出世。可谓百花齐放,百家争鸣啊。喜欢这种氛围,呵呵。

今天我不谈反射,但和反射有关

不谈吉日,但话题是从这里开始

吉日的《白话反射》里说到:

"我们在开发大型软件项目时经常会遇到,系统很庞大了有几百M的代码了,主程序启动时,总不能把这些都引用了吧?全部加载在内存里?那程序的启动速度,不知道会不会慢如老牛推车了?这时候也会用一些反射技术等,用到哪个窗体,就动态加载哪个那个窗体,总感觉比较清爽一些。"

表面一看,确实。主模块引用这么多模块,启动的时候那不是很慢,那必须用反射来“制造”一个延迟加载的机制。

实际上,CLR的加载过程是什么样子的呢?(不说的那么详细)

很多书里都介绍了,JIT的编译单元是方法。当执行一个方法之前,会先JIT(即时编译)这个方法,然后发现,这个方法里引用了在别的程序集里定义的类型,那么CLR Loader就会加载这个程序集(更详细的过程可以参见《.Net本质论》或我之前的两篇文章CLR Loader和Assembly Loader)。那这么看来,吉日说的就是错误的,几百M的代码不错,难道这几百M的代码在启动的时候就会全部执行么?如果不执行也就不会JIT,不会JIT也就不会加载这些模块。但是有一个例外:你这几百M代码,全部放在一个模块里(我觉得吉日肯定不会那么干)。

根据上面的讨论,所以吉日说的这种场景不需要用反射,不会影响主程序的启动速度,只需要合理的划分子功能模块就可以了,不要弄得铁板一块。

真的是这样的么?如果光说教我会很快的陷入太虚之中,所以我准备了一个小实验:

image

这个一个典型的WinForm应用的解决方案。Main是主启动项目,而FunctionModule1-FunctionModule4是具体的子功能模块(实际的项目中,肯定不止四个)。

我们看到这里Main项目中引用了四个子功能模块。然后下面是主界面:

image

Function主菜单下有四个子菜单,每个子菜单调用一个子功能模块,菜单的事件代码如下:

   1: private void function1ToolStripMenuItem_Click(object sender, EventArgs e)
   2: {
   3:     Function1 function = new Function1();
   4:     MessageBox.Show(function.ToString());
   5: }
   6: private void function2ToolStripMenuItem_Click(object sender, EventArgs e)
   7: {
   8:     Function2 function = new Function2();
   9:     MessageBox.Show(function.ToString());
  10: }
  11: private void function3ToolStripMenuItem_Click(object sender, EventArgs e)
  12: {
  13:     Function3 function = new Function3();
  14:     MessageBox.Show(function.ToString());
  15: }
  16: private void function4ToolStripMenuItem_Click(object sender, EventArgs e)
  17: {
  18:     Function4 function = new Function4();
  19:     MessageBox.Show(function.ToString());
  20: }

现在问:这里的Main项目启动时,会主动加载这四个子功能模块么?

我们使用Visual Studio调试时输出窗口的功能看看:

image

窗口中,前部分输出的是加载的框架的模块,最后一个是加载Main程序集。但是FunctionModule1-FunctionModule4呢?我仔仔细细寻找了好几遍没发现。那么说明,Main项目启动时,不会主动加载子功能模块的,所以吉日的说法是错误的,除非他把所有子功能模块全部写在主功能模块中。那什么时候加载这些子功能模块?来,我们尝试点一下菜单:

image

子菜单的功能弹出来了,从输出窗口里也发现这个时候加载了FunctionModule1.dll。

以下是对WinForm应用架构设计的讨论

-----------------------------------------------------------------------------------------------------------

下面,我们来讨论一下WinForm应用的架构问题。

我不知道园子里有多少人在做.Net WinForm应用,只是看到园子里大部分人都是在搞Web或WPF。苦恼的我还在搞WinForm,不过也其乐无穷。

上面的实例实际上也给出了大部分WinFrom项目的一个雏形,很多的菜单,每个菜单对应一个功能,打开一个功能的时候要么是Mdi的模式,弹出一个窗口,要么就是一个TabPage。大的工程肯定是一个人完成不了的,需要多个人来做这个事情。但是又需要统一规划。

所以必定是这样的:由架构师来规划好底层支撑的框架,比如这里的Shell,还有一些引擎级别的服务(比如LanguageService“多语言”,PropertyService“持久化软件里某些设置”等)。这个框架必须稳定,项目组其他人员,每个人负责一个或多个功能,每个人只需要关注自己的业务就可以了,把功能做好,然后调用框架里提供的一些服务,把自己给挂接到主框架中。

那么如果按照上面示例的这种设计,我们的程序主框架就必须引用所有的子功能模块,项目开始的时候,可能只有一两个模块,随着项目的前进,引用也在不断的增加,而主界面上的菜单也在不断的增加,这可以说主界面是稳定的么?

那我们必须寻找一种机制。子功能对自己负责,子功能负责自己将自己注册到系统中去。这个时候我们可能采用这样的方式:

一个Modules文件夹,在这个文件夹下又放着很多子文件夹,每个子文件夹里放着一个功能,当系统启动的时候,由框架搜寻Modules子文件夹,在里面查找一个后缀名为addin(或者其他方式)的xml文件,文件里面的内容可能如下:

<MenuItem Site=”File” Text=”Edit” Icon=”Edit.png” CommandType=”MultiLibrariesApp.EditCommand” />

主模块读取这个之后就会生成一个菜单项,当点击这个菜单项的时候,根据CommandType,利用反射实例化一个EditCommand类型,所有的Command可能都实现一个ICommand接口,而这个接口里有一个Run方法:

   1: public interface ICommand
   2: {
   3:     void Run();
   4: }
   5:  
   6: public class EditCommand : ICommand
   7: {
   8:     public void Run()
   9:     {
  10:         //code here...
  11:     }
  12: }
  13: public class MenuItem
  14: {
  15:     public string Text{get;set;}
  16:     //主框架的代码里
  17:     ICommand command = null;
  18:     private void MenuItem_Click(object sender, EventArgs e)
  19:     {
  20:             //延迟加载
  21:             if(command == null)
  22:                 command = //通过反射实例化具体的Command类型,这里就是EditCommand
  23:            command.Run();
  24:     }
  25:     public ToolStripMenuItem CreateMenu()
  26:     {
  27:         ToolStripMenuItem menuItem = new ToolStripMenuItem();
  28:         menuItem.Text = this.Text;
  29:         menuItem.Click += MenuItem_Click;
  30:     }
  31: }

这样,就能保证主框架是稳定的了,子功能负责自己的菜单管理,只需要写一个文件就可以了。

当然,实际的框架实现过程中,肯定会碰到各种各样的问题,这只是一个思路。对于WinForm程序而言,你还可以参考一下开源项目:

SharpDevelop 一个开源的IDE

MonoDevelop 从SharpDevelop发展而来,但是现在大变样了

Mono.AddIn由MonoDevelop的插件机制发展出来的一个小插件系统

Composite Application Block 微软模式&实践小组的

还有很多其他,我就没有研究过了。

以上只是我的一个思路,由于WinForm开发的看到不多,我在网上搜WinForm Best Practice也没找到多少资料,所以希望能够在这方面有所讨论。也许能碰撞出一些火花出来。

后话

刚才出去了一趟,在路上又思考了一些问题。

通过上面对模块加载和WinForm架构设计的讨论,总结一下:吉日文中说的这种应用反射的情况并不是因为基于启动效率的问题,而是设计的考量。这个地方跟性能一点关系都没有(在这里对我在老赵博客里开始错误的评论表示道歉),用不用反射启动效率都是这样。

再看看吉日另外一个应用反射的场景:两个类循环引用。我不知道为什么有这样一个设计,如果是遗留代码那你首先应该考虑重构一下,如果实在不能重构,就必须这样,那只有用反射了。所以这个问题也是设计上的问题,跟反射也没啥关系。

还有吉日说的,配置多数据库的场景。老赵说了,这里推崇ORM。即使你不用ORM,我也觉得这是没有必要的。我不知道有多少情况一个正在运行的系统要突然更换不同类型的数据库?即使有这种情况,那么这也属于重大变更,对于这种变更,你完全可以修改代码。还有,针对这种情况,微软已经给出了Best Practice:提供者模式。虽然提供者模式最终还是反射。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/549366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++猜单词游戏

游戏说明 运行须知&#xff1a; 请将压缩文件中的list.txt englishi.txt people.txt answer.txt放入D盘中的game文件夹中才能正常运行游戏&#xff0c;或者更改文件途径。 输入1可以登录用户&#xff0c;输入2可以游客试玩&#xff0c;用户登录可以查看单词本&#xff0c;里面…

JavaScript工具

在线 JavaScript格式美化工具 http://jsbeautifier.org/ 在线JS调试工具 http://jsbin.com/ 脚本兼容检查 http://www.thefrontside.net/crosscheck Komodo Edit http://www.activestate.com/komodo_edit/ Venkman Development http://www.hacksrus.com/~ginda/venkman/

Oracle分析函数一——函数列表

Oracle 分析函数 Oracle 分析函数——函数列表 SUM &#xff1a; 该函数计算组中表达式的累积和 MIN &#xff1a; 在一个组中的数据窗口中查找表达式的最小值 MAX &#xff1a; 在一个组中的数据窗口中查找表达式的最大值 AVG &#xff1a;…

这次,被游戏玩了

这次&#xff0c;我被游戏玩了 &#xff0c;我的一个朋友在玩台服的未来启示录&#xff0c;看那个场景有点像天堂。他的配置为Intel p43.0, gigabyte 915, DDR400512*2牌子忘记了&#xff0c;但也不错不是杂牌的, maxsun 9500GT TC512高清版&#xff0c;WD160G SATA。玩是…

用MATLAB实现神经网络

一 BP神经网络实现不使用MATLAB神经网络工具箱问题分析MATLAB实现代码运行结果绘制的图像 二 使用MATLAB的神经网络工具箱简易实现BP网络问题分析工具箱中的相关函数一些参考了MATLAB自带的英文手册mapminmax函数newff函数新版本关于nettrainParam的常用属性train函数sim函数 M…

Follow Me:CCIE RS--使用小凡模拟器搭建的CCIE拓扑图

我用小凡模拟器搭建了CCIE LAB 拓扑图有何不对的地方请指正转载于:https://blog.51cto.com/tanfo/216831

【积累】非常全面的开源数据集

非常全面的开源数据集 由skymind.ai公布 非常全面的开源数据集最近新增数据集自然图像数据集地理空间数据人工数据集人脸数据集视频数据集文本数据集问答数据集情感数据集推荐和排名系统语音数据集音符音乐数据集健康 &生物数据政府&统计数据网络和图形其它数据集 最近…

女性最常说的5大谎言:

女性最常说的5大谎言&#xff1a; “没事&#xff0c;我很好” “哦&#xff0c;这不是新买的&#xff0c;买很久了” “这不是很贵” “这是打折时买的” “我正在路上” 我好像都曾听过&#xff01; 摘自&#xff1a;http://lady.163.com/special/00261MPK/kexue004.html

【资源】史上最全数据集汇总

无论是数据挖掘还是目前大热的深度学习&#xff0c;都离不开“大数据”。大公司们一般会有自己的数据&#xff0c;但对于创业公司或是高校老师、学生来说&#xff0c;“Where can I get large datasets open to the public” 是一个不得不面对的问题。 本文将为您提供一个网站/…

1030利用三层交换机实现VLAN间通信

实验相关文件在附件中转载于:https://blog.51cto.com/network0546/219543

非线性最优化(二)——高斯牛顿法和Levengerg-Marquardt迭代

高斯牛顿法和Levengerg-Marquardt迭代都用来解决非线性最小二乘问题(nonlinear least square)。 From Wiki The Gauss–Newton algorithm is a method used to solve non-linear least squares problems. It is a modification of Newtons method for finding a minimum of a …

为右键新建菜单添加内容

右键菜单的内容由注册表关联,通过修改注册表就可以定制鼠标右键快捷菜单中的“新建”菜单所包含的项目。 需要注意的是&#xff0c;在修改注册表以前要先将注册表备份&#xff0c;以免出现问题时无法恢复。 一、增加菜单项目1&#xff0e;首先&#xff0c;决定要增加到菜单中的…

逆透视变换详解 及 代码实现(一)

逆透视变换详解 及 代码实现&#xff08;一&#xff09; 中主要是原理的说明&#xff1a; 一、世界坐标轴和摄像机坐标轴 从下图中可以看到&#xff0c;世界坐标为(X,Y,Z) 相机坐标为(Xc,Yc,Zc) 而世界坐标变换到相机坐标存在一个旋转矩阵变换R以及一个位移变换T。 根据上图…

C调用C++链接库

C调用C链接库&#xff1a; 1.编写C代码&#xff0c;编写函数的时候&#xff0c;需要加入对C的接口&#xff0c;也就是extern “c" 2.由于C不能直接用"class.function”的形式调用函数&#xff0c;所以C中需要为C写一个接口函数。例如本来要调用student类的talk函数&a…

逆透视变换详解 及 代码实现(二)

根据 逆透视变换详解 及 代码实现(一)的原理 下面我用车上拍摄的车道图像&#xff0c;采用逆透视变换得到的图像&#xff0c;给出代码前我们先看下处理结果。 首先是原始图像&#xff1a; 下图为逆透视变换图像&#xff1a; 下面说具体的实现吧&#xff01;&#xff01; 一、…

[赵星理]《简单男人》--歌曲温暖你的心,激励你前进

简单的男人&#xff0c;简单的歌曲&#xff0c;赵星理《简单男人》送给所有身负家庭责任的人&#xff0c;要让家越来越美&#xff0c;再苦再累也不能后退。加油&#xff01;简单男人词曲&#xff1a;赵星理演唱&#xff1a;赵星理累不累也不许落泪醉不醉苦辣都值得回味要让家越…

ArrayList list = new ArrayList(20);中的list扩充几次

16. ArrayList list new ArrayList(20);中的list扩充几次&#xff08;&#xff09; A 0 B 1 C 2 D 3 答案&#xff1a;A ArrayList动态扩容机制 初始化&#xff1a;有三种方式 默认的构造器&#xff0c;将会以默认的大小来初始化内部的数组&#xff1a;public …

利用ATL创建com组件和如何在程序中使用组件的接口函数和设置接口的属性

这是一个ATL开发实例的流程&#xff1a; 1. 在atl中插入一个atl实例&#xff0c;然后添加一个类&#xff0c;派生自ccmdtarget。 2. 添加相应的属性或者方法&#xff0c;在这里需要明白一点的是&#xff0c;这个属性和方法其实是一个概念&#xff0c;只是添加一个属…

java 课外练习题

1.下列哪些是Thread类中的方法&#xff08;&#xff09; A start() B run() C exit() D getPriority() 解析&#xff1a;线程的就绪状态&#xff1a;新建线程后start()方法的调用&#xff0c;来启动线程&#xff0c;但此时线程并没有进入运行状态&#xff0c;只是…

关于ubuntu 是否需要使用std::到问题。

首先是在ubuntu下编译c要使用g&#xff0c;比如 g -Wall 01knap2.cpp -o 01knap2 而如果是编译c语言文件的话则需要用 gcc -Wall 01knap2.c -o 01knap2 即可&#xff0c;g编译得到的是.cpp文件&#xff0c;而gcc编译得到的是.c文件。 第二个是关于是否能够直接使用cout&#…