我现在陷入了一个“高级”ASP.NET课程,我的导师只是提出了一个我不确定的观点。 (我说“高级”,因为他仍然使用HTML表格进行页面布局,我们刚刚讨论了Master Pages非常高级的主题。我需要眼睛漂白!)
他声明,而不是创建一个包含所有适用的数据和方法的Person类,你应该创建一个Person结构和Person类。该结构包含通常为Person类的属性,该类仅包含方法。由于Person结构位于堆栈上,因此只要您的方法或任何弹出堆栈而不是像堆栈一样在堆上进行垃圾收集,与您的人员关联的数据就会消失。
这应该可以节省内存并使垃圾收集过程更快。
问题是:这会产生多大的影响并且真的值得吗?
答案 0 :(得分:10)
说实话,我不相信你会从这样做中看到很多性能上的好处。结构难以写好,编写糟糕的结构比对垃圾收集器征税要危险得多。
听起来你的教授主张使用data transfer objects来鼓励国家和行为的分离。如果正确完成,这可能是一个很好的设计(在大多数情况下,您将使用类而不是结构来实现此模式)。
我之所以说使用结构可能更危险的原因是CLR处理的值类型更加不同,如果写得不好(例如可变结构)会产生可怕的麻烦。此外,如果您的类型包含许多字段,然后从一个方法传递到另一个方法,那么您将在每个方法调用上复制每个字段的值,因此使用的内存比您在第一次使用类时的内存更多。
答案 1 :(得分:7)
针对您描述的模式提出的推理有几个问题。
首先,结构不是始终在堆栈中分配。它们仅在方法中的参数或本地实例时进行堆栈分配。定义为类成员的结构实际上是堆分配的。因此,由于减少垃圾收集的努力,结构更有效的论点仅在某些狭窄的上下文中才是正确的。
其次,结构的任何非ValueType成员也在堆上分配(例如字符串)。因此,即使结构可以简单地从堆栈中弹出,它引用的任何堆对象仍然必须进行垃圾回收。
第三,结构很少会节省内存,因为当它作为方法参数传递时,它们具有值语义。这基本上意味着创建并传递了结构的副本,而不是对现有结构的引用 - 这在内存上的成本也不低。当你看到其他响应表明可变结构可能有问题时 - 原因(以及其他一些结果)由于结构是按值传递的,因此对原始结构的更改不适用于制作副本的位置结构。这可能会导致您违反程序中的假设或期望,在该程序中您可能实际需要结构的引用语义。
<小时/> 就个人而言,我发现创建DTO对象(无论是类还是结构)的模式在业务层对象中嵌套是一个似乎没有多大好处的模式。除非你有一个持久性或表示层专门利用这种模式来跨层传递信息,否则我看不到太多价值。
此外,使用结构作为DTO对象的特定情况似乎存在缺陷,因为它引入了不必要的冗余。由于结构不能继承其他结构,因此无法表达is-a关系。当您拥有继承自Person的Customer时,您会怎么做。您是否重复了Customer结构中的所有Person属性?您是否在Customer结构中嵌入Person结构?这两种方法都不理想。如果您至少使用过类,则可以让客户扩展Person。
答案 2 :(得分:2)
这对我来说听起来很傻。我不确定在HTML中使用表格进行布局的人的编码建议中有多少库存。如果这是一种产生显着优势的技术,那么它就是一种标准做法。事实并非如此。这是我第一次听说这种技术。
答案 3 :(得分:1)
无论该策略是否更有效,都会更难以合作,我会尽力避免。话虽如此,我怀疑这会更有效率。如果是,编译器可能会自动以这种方式实现它。依靠编译器进行语言级优化通常是安全的。
答案 4 :(得分:0)
老实说,这是我第一次听说这种做法。
据我所知,结构是值类型。通常,如果结构少于16个字节,则需要定义结构,不可变,不必加箱,并表示类似于基本类型的单个值。
MSDN, where Microsoft has published structure design guidelines.
像教练所说的类将轻松地扩展超过16字节规则。并且很难看到这种性质的结构没有封装属性中的业务(或者至少是验证)规则。在我看来,这是一个非常粗制滥造的设计。
答案 5 :(得分:-2)
这种方法的问题是对象(类的实例化)具有可变状态。相反,结构应该是不可变的,这意味着它们没有可以改变的状态。在典型的应用程序中,业务层大量利用对象状态的变化(即工作流程,验证等)。
他所描述的好处并不能保证这会带来的弊端。