1
14
我将给出一个实际的例子,其中使用了许多(非常)小的程序集生成了.NET DLL地狱。 在工作中,我们有一个大型的本土框架,它是一个很长的牙齿(.net 1.1)。除了常见的框架类型管道代码(包括日志记录、工作流、队列等),还有各种封装的数据库访问实体、类型化数据集和其他一些业务逻辑代码。我没有参与这个框架的初始开发和后续维护,但继承了它的使用。正如我提到的,整个框架产生了许多小的DLL。当我说很多的时候,我们说的是100个以上——不是你提到的可管理的8个左右。更为复杂的是,这些程序集都是经过stronly签名、版本控制并出现在GAC中。 所以,几年后快速前进,几次维护周期之后,发生的事情是,对DLL及其支持的应用程序的相互依赖造成了严重的破坏。在每台生产机器上,machine.config文件中都有一个巨大的程序集重定向部分,确保通过Fusion加载“正确”的程序集,无论请求什么程序集。这源于重建依赖于修改或升级的框架和应用程序集时遇到的困难。为了确保在修改程序集时不会对其进行破坏性更改,通常会付出很大的努力。程序集已重建,并在machine.config中创建了新的或更新的条目。 我会停下来听一声巨大的集体呻吟和喘息! 这个特定的场景是“不做什么”的海报。事实上,在这种情况下,你会陷入一种完全无法挽回的境地。我记得在我第一次开始使用这个框架的时候,我花了2天的时间来完成我的机器的开发设置——解决我的GAC和运行时环境的GAC之间的差异,machine.config程序集重定向,编译时由于引用不正确而导致的版本冲突,或者更可能是由于直接引用而导致的版本冲突。重新命名组件A和组件B,但组件B引用了组件A,但与我的应用程序的直接引用不同。你明白了。 这个特定场景的真正问题是程序集内容太过细化。这最终导致了相互依赖的错综复杂的网络。我的想法是,最初的架构师认为这将创建一个高度可维护的代码系统——只需要重新构建系统组件的非常小的更改。事实上,恰恰相反。此外,对于这里已经发布的其他一些答案,当您了解到这个数量的程序集时,加载大量的程序集确实会导致性能下降——当然是在解析期间,我猜,尽管我没有经验证据,但是在某些边缘情况下,运行时可能会受到影响,特别是在可能发生反射的情况下。玩——在那一点上可能是错误的。 你可能会认为我会被鄙视,但我相信程序集有逻辑物理分离——当我在这里说“程序集”时,我假设每个DLL有一个程序集。归根结底就是相互依赖。如果我有一个依赖于组件B的组件A,我总是问自己是否需要在组件A外引用组件B。或者,这对分离有好处吗?查看如何引用程序集通常也是一个很好的指示器。如果要将大型库划分为程序集A、B、C、D和E。如果90%的时间内都引用程序集A,因此,始终必须引用程序集B和C,因为A依赖于它们,那么最好将程序集A、B和C组合在一起,除非有一个真正令人信服的理由允许它们组合在一起。保持分离。企业库是一个典型的例子,为了使用库的一个方面,您几乎总是需要引用3个程序集——然而,对于企业库来说,基于核心功能和代码重用构建的能力是其体系结构的原因。 查看架构是另一个很好的指南。如果您有一个整洁的堆叠结构,您的程序集依赖关系是以堆栈的形式存在的,比如说“垂直”,而不是“Web”,当您在每个方向上都有依赖关系时,它就开始形成,那么在功能边界上分离程序集是有意义的。否则,就要把事情变成一个整体,或者重新设计。 不管怎样,祝你好运! |
2
28
由于性能分析比预期的要长一些,所以我把它放进了它自己的独立答案中。我将接受彼得的正式回答,尽管它缺乏测量,因为它是最有助于激励我自己进行测量的工具,而且它给了我对可能值得测量的东西最有启发。
分析:
这个分析完全忽略了一些人在回答中提到的“设计质量”,因为我不认为质量是这个权衡中的一个变量。我假设开发人员首先让他们的实现以获得最佳设计的愿望为指导。这里的权衡是,为了(某种程度的)性能,是否值得将功能聚合为比设计严格要求的更大的程序集。
应用程序结构
:
该应用程序包含1000个类,分为200组,每组5个类彼此继承。类的名称为axxx、bxxx、cxx、dxx和exxx。类A是完全抽象的,B-D是部分抽象的,覆盖了每个方法中的一个,E是具体的。这些方法的实现使得对e实例的一个方法调用将在层次结构链上执行多个调用。所有方法体都非常简单,理论上它们都应该是内联的。 这些类分布在8个不同配置的组件中,沿2个维度分布:
测量结果并非都是精确测量的;有些是用秒表测量的,误差范围较大。测量结果如下:
结果:
观察:
因此,从所有这些看来,更多程序集的负担主要由开发人员承担,然后主要以编译时间的形式承担。正如我已经说过的,这些项目非常简单,以至于每一个编译花费的时间都不到一秒钟,这就导致了每个程序集的编译开销占主导地位。我可以想象,跨大量程序集的次二级程序集编译强烈地表明这些程序集被拆分得比合理程度更高。另外,在使用预编译的程序集时,主要的developer参数 反对 拆分(编译时间)也会消失。 在这些度量中,我几乎看不到为了运行时性能而反对拆分为较小的程序集的任何证据。唯一需要注意的是(在某种程度上)尽可能避免跨越继承;我想大多数理智的设计都会限制这一点,因为继承通常只发生在一个功能区域内,而这个功能区域通常会在单个程序集中结束。 |
3
4
加载每个程序集(如果它们被签名的话甚至更多)会对性能造成轻微的影响,因此这是在同一个程序集中将常用的东西聚集在一起的一个原因。我不认为加载东西后会有很大的开销(尽管在跨越装配边界时,JIT可能会遇到一些静态优化问题)。 我尝试采用的方法是:名称空间用于逻辑组织。程序集将物理上应一起使用的类/命名空间分组。也就是说,如果你不希望得到A类而不是B类(反之亦然),它们属于同一个集合。 |
4
2
单片怪兽使得重用代码的一部分在以后的工作中比原来更昂贵。并且会导致不需要耦合的类之间的耦合(通常是显式的),这会导致更高的维护成本,因为测试和错误更正将更加困难。 拥有许多项目的缺点是(至少在vs中)与少数项目相比,编译需要相当长的时间。 |
5
2
装配组织中最大的因素应该是类和装配级别上的依赖关系图。 程序集不应具有循环引用。这应该是很明显的开始。 相互依赖性最大的类应该在单个程序集中。 如果A类依赖于B类,而B可能不直接依赖于A,那么它不太可能在A之外使用,那么它们应该共享一个组件。 您还可以使用程序集来强制分离关注点—将您的GUI代码放在一个程序集中,而您的业务逻辑放在另一个程序集中,这将提供某种程度的业务逻辑的强制执行,使您的GUI不可知。 基于代码运行位置的程序集分离是另一个需要考虑的问题-可执行文件之间的公共代码(通常)应该在公共程序集中,而不是让一个.exe直接引用另一个.exe。 也许您可以使用程序集进行的更重要的事情之一是区分公共API和内部用于使公共API工作的对象。通过将一个API放入单独的程序集中,可以强制其API的不透明性。 |
6
1
我想如果你只说一打,你应该没事的。我正在开发一个100多个程序集的应用程序,它是 非常 痛苦的 如果您没有某种方式来管理依赖关系——知道修改程序集X会破坏什么,那么您就有麻烦了。 我遇到的一个“好”问题是,当程序集A引用程序集B和C时,B引用程序集D的v1,而C引用程序集D的v2时。(“Twisted Diamond”将是一个很好的名称) 如果您想拥有一个自动化的构建,那么您将很有兴趣维护构建脚本(它需要按照依赖项的相反顺序构建),或者拥有“一个解决方案来管理所有脚本”,如果您拥有大量的程序集,那么在Visual Studio中几乎不可能使用该解决方案。 编辑 我认为您的问题的答案在很大程度上取决于程序集的语义。不同的应用程序是否可能共享程序集?是否希望能够分别更新两个应用程序的程序集?你打算用海关总署吗?或者复制可执行文件旁边的程序集? |
7
0
就我个人而言,我喜欢单一的方法。 但有时您无法帮助创建更多程序集。当需要公共接口程序集时,.NET远程处理通常对此负责。 我不知道装载组件的开销有多大。(也许有人能启发我们) |
Emopusta · 从后端到前端的图像路径不工作 2 年前 |
Asdrubal Hernandez · Linq查询特定数组索引出错 2 年前 |
Niyazi Babayev · 如何在表达式中动态应用表达式? 2 年前 |
Dansih · .Net核心自定义身份验证方案 2 年前 |
lolorekkk · 面板插入。NET WinForm 2 年前 |