DTO 类与结构
Posted
技术标签:
【中文标题】DTO 类与结构【英文标题】:DTO classes vs. struct 【发布时间】:2012-06-16 09:19:23 【问题描述】:所以,这实际上是这个问题是我目前的基石。 我正在重构我的个人项目,尝试提高性能,优化内存使用,使代码简单明了。我有不同的应用程序层(实际上是 DAL、BLL、ServiceAgents,它们是 WCF 服务)。我正在使用实体/模型/DTO 在这些层之间传递数据,这是无状态的(根本没有任何逻辑)。目前它们是这样的对象:
public class Person
public ComplexId Id get; set;
public string Name get; set;
// ...
我曾经使用过这种方法,这就像“最佳实践”,是吗?这是存储传输数据的最佳方式吗?如果我像这样为 struct 更改它会怎样:
public struct Person
public ComplexIdentifier ComplexId;
public string Name;
public struct ComplexIdentifier
public int LocalId;
public int GlobalId;
从性能/内存使用的角度来看,这会是更好的方法吗?或者也许有某种陷阱在起作用?
【问题讨论】:
"class"(这很容易;p) 感谢大家的参与! 【参考方案1】:对于标准 DTO 实体,您需要坚持使用该类。
与类相比,struct
的潜在用例范围有限。当struct
类型变得太大时也会出现效率问题(不要忘记,它们是值类型,并且在传递时被复制),如MSDN guidelines about value types 中所述。更不用说当您开始通过属性公开struct
类型时,或者在引用接口时不小心将其装箱,或者使它们可变时会遇到大量...
我不是 saying not to use struct
,但我非常很少发现自己需要在我们的主桌面应用程序中使用 struct
类型 - 它是分层的并具有 DTO类型。
性能问题不能像
struct
与class
那样简单地回答。您将需要使用诸如dotTrace 或ANTS 之类的分析工具来找到热点并从那里开始。性能问题并非微不足道,好的工具通常是答案的开始。
【讨论】:
+1。一个补充:Mutable structs are EVIL!! @Steven 当然,我已经在我的回答中添加了那个链接——有点潜意识;-) 同意。为了减轻一些关于将值复制到这些对象和从这些对象复制的问题,请利用 [AutoMapper](automapper.org)。 用作数据传输对象的可变类比可变结构更邪恶。【参考方案2】:数据传输对象的两个最佳选择通常应该是深度不可变的类对象,或者具有适合用作数据传输对象的类型的暴露字段的结构。其他类型的结构体有时也可以使用,但暴露字段结构体是迄今为止最简单的,简单是一大优点。
可变结构是邪恶的概念可以追溯到一些早期的 C# 编译器,其中
SomeReadonlyStruct.SomeProperty = 5;会被编译器默默地转换成:
var temp = SomeReadonlyStruct; temp.SomeProperty = 5;在只读属性后面隐藏结构字段是为了确保前一条语句拒绝编译而不是产生损坏的代码。由于较新的编译器将拒绝改变只读结构的可写字段,因此不再需要将字段包装在只读属性中。
与其他类型的数据传输对象相比,具有公开字段的结构具有巨大的优势:每个结构都具有适合数据传输的类型的公开字段,并且除了构造函数之外没有其他成员的行为方式相同,这不足为奇.从未使用过结构体的人可能会对它们的行为不像类这一事实感到有些惊讶,但了解任何此类结构体如何工作的人会理解它们都是如何工作的。
考虑以下代码:
customerPhone = someDataSource.GetPhoneNumber(customerID); customerPhone.Extention = "5309"有些人不喜欢如果customerPhone
是一个暴露字段结构,设置Extension
属性不会影响someDataSource
中的信息。虽然编写 struct 字段确实不会更新任何其他内容,但这比 customerPhone
是可变类类型时的情况要好得多。任何了解customerPhone
是暴露字段结构类型的人都会知道对其成员的更改不会影响其他任何内容。相比之下,如果customerPhone
是可变类类型,则上述代码可能会通过更改与customerID
关联的电话号码来更新someDataSource
。或者它可能不会。或者,如果一个电话号码与两个 customerID
值相关联,则上述代码可能会同时更改它们。为了确定上述代码可能产生的影响和副作用,可能需要研究的代码量会非常大。更糟糕的是,可能很难确定一个人没有遗漏任何东西。
肯定有一些地方传递类对象引用比传递结构更有效。还有一些情况,可变类对象可以成为类中有用的数据持有者。然而,一些通用包装器可以方便地将结构包装在可变或不可变类中,并在这些类型的类之间交换信息。
接口 IReadableHolder如果没有可变结构,这样的结构会更加尴尬。
【讨论】:
【参考方案3】:从性能/内存使用的角度来看,这会更好吗?
没有。结构是值类型,因此它们是按值复制的。这意味着您将始终复制完整对象,而不是仅复制引用。如果有的话,它可能会稍微慢一些并使用更多内存(因为对象会被复制)。
您不应该这样做的另一个原因:应始终避免使用可变结构,因为如果您不小心,它们可能会导致意外行为。
【讨论】:
从那时起,引入了只读结构,并且通过引用传递已扩展为返回值和带有“in”关键字的只读引用参数。【参考方案4】:struct
可能更危险的是,CLR
对值类型的处理方式更加不同,如果写得不好(例如可变结构)可能会让人头疼。 此外,如果您的类型包含许多字段,然后从一个方法传递到另一个方法,那么您将在每个方法调用中复制每个字段的值,因此比您首先使用一个类时使用更多的内存。
使用DTO(数据传输对象)将鼓励状态和行为的分离。如果做得好,这可能是一个很好的设计(在大多数情况下,您将使用类而不是结构来实现此模式)。
此外,使用struct
作为DTO object
的具体情况似乎存在缺陷,因为它引入了不必要的redundancy
。由于structs cannot inherit other structs
,您无法表达is-a 关系。当你拥有继承自Person
的Customer
时,你会怎么做。您是否重复所有 Person properties in the Customer
结构?您是否将 Person 结构嵌套在 Customer 结构中?这两种方法都不理想。如果您至少使用了类,您可以让 Customer 扩展 Person。
【讨论】:
【参考方案5】:您应该使用类方法,结构方法甚至可能更慢,因为结构是值类型,并且无论您将它们作为函数参数传递到何处,都会进行复制。因此,对于您的 DTO,请坚持使用课堂方法。
【讨论】:
【参考方案6】:我的意见是:课程更好。
通过类,您可以实现 INotifyPropertyChanged 接口,如果您进行数据绑定,这将非常有用。
你也可以有一些私有字段,比如“ID”,只有“get”-property, 您可以向您的类添加一些方法,您可以使您的实体可序列化。 我只想说,你对类更灵活,我不认为在这种情况下结构和类之间有任何很大的性能差异。
这是一本非常好的书,它通过设计 nTier 应用程序帮助了我很多: http://msdn.microsoft.com/en-us/library/ee817644.aspx
看看那里。它说: “在结构和类之间进行选择。对于不支持的简单业务实体 包含分层数据或集合,考虑定义一个结构来表示 商业实体。对于复杂的业务实体,或对于业务实体 需要继承,而是将实体定义为类。”
【讨论】:
我正在使用无状态类 - 我知道 INotifyPropertyChanged 但根据我的任务我不需要它。我也不需要向这些对象添加任何逻辑(方法)。并且只获取属性 - 结构可以具有只读字段,可以吗?以上是关于DTO 类与结构的主要内容,如果未能解决你的问题,请参考以下文章