25

So, this is actually this question is my current keystone. I'm working on refactoring of my personal project, trying increase performance, optimize memory usage, make code easy and clear. I have a different application layers (actually, DAL, BLL, ServiceAgents which is WCF services). I'm using Entities/Models/DTOs to pass data between those layers, which is stateless (don't have any logic at all). Currently they're objects like this:

public class Person
{
  public ComplexId Id { get; set; }
  public string Name { get; set; }
  // ...
}

I used to use such approach, it's like a "best practice", is it? Is this best way to store transferred data? What if I change it for struct like this:

public struct Person
{
    public ComplexIdentifier ComplexId;
    public string Name;
}

public struct ComplexIdentifier
{
    public int LocalId;
    public int GlobalId;
}

Is this would be better way from performance/memory usage perspective? Or maybe there is some kind of traps acting that way?

4

6 回答 6

27

对于标准 DTO 实体,您需要坚持使用该类。

与类相比, A的潜在用例范围struct有限得多。struct当类型变得太大时也会出现效率问题(不要忘记,它们是值类型,并且在传递时会被复制),如MSDN 关于值类型的指南中所述。更不用说当您开始通过属性公开类型时的大量陷阱,或者在引用接口时意外地将其装箱,或者使它们可变......struct

我并不是 struct在相关时不使用,但我很少发现自己需要struct在我们的主桌面应用程序中使用类型——它是分层的并具有 DTO 类型。


性能问题不能像structvs.那样简单地回答class。您将需要使用dotTraceANTS等分析工具来找到热点并从那里开始。性能问题并非微不足道,好的工具通常是答案的开始。

于 2012-06-13T12:11:46.007 回答
7

数据传输对象的两个最佳选择通常应该是深度不可变的类对象,或者具有适合用作数据传输对象的类型的暴露字段的结构。其他类型的结构有时也可以使用,但暴露字段结构是迄今为止最简单的,简单是一大优点。

可变结构是邪恶的概念可以追溯到一些早期的 C# 编译器,其中

  SomeReadonlyStruct.SomeProperty = 5;

编译器会默默地将其转换为:

  var temp = SomeReadonlyStruct;
  temp.SomeProperty = 5;

将结构字段隐藏在只读属性后面是为了确保前一条语句拒绝编译而不是产生损坏的代码。由于较新的编译器将拒绝改变只读结构的可写字段,因此不再需要将字段包装在只读属性中。

与其他类型的数据传输对象相比,具有公开字段的结构具有巨大的优势:每个结构都具有适合数据传输的类型的公开字段并且除了构造函数之外没有其他成员的行为方式相同,这不足为奇。从未使用过结构的人可能会对它们的行为不像类这一事实感到有些惊讶,但了解任何此类结构如何工作的人会理解它们是如何工作的。

考虑以下代码:

  customerPhone = someDataSource.GetPhoneNumber(customerID);
  customerPhone.Extention = "5309"

有些人不喜欢 ifcustomerPhone是一个暴露字段结构,设置Extension属性不会影响someDataSource. 虽然编写 struct 字段确实不会更新任何其他内容,但这种情况比customerPhone可变类类型要好得多。任何了解这customerPhone是暴露字段结构类型的人都会知道对其成员的更改不会影响其他任何内容。相比之下, ifcustomerPhone是一个可变类类型,像上面这样的代码可能会someDataSource通过更改与它关联的电话号码来更新customerID。或者它可能不会。或者,如果一个电话号码与两个相关联customerID值,上面的代码可能会改变它们。为了准确确定上述代码可能产生的影响和副作用,可能需要研究的代码量非常大。更糟糕的是,可能很难确定一个人没有错过任何东西。

肯定有一些地方传递类对象引用比传递结构更有效。还有一些情况,可变类对象可以成为类中有用的数据持有者。然而,一些通用包装器可以方便地将结构包装在可变或不可变类中,并在这些类型的类之间交换信息

接口 IReadableHolder<T> { T 值 {get;} }

类 MutableHolder<T> : IReadableHolder<T>
{
  公共 T 值;
  IReadableHolder.Value {get {return Value;} }
  公共 MutableHolder(T newValue) { Value = newValue; }
  公共 MutableHolder(IReadableHolder<T> it) { Value = it.Value; }
}
类 ImmutableHolder<T> : IReadableHolder<T>
{
  T_值;
  公共价值 {get {return _Value;} }
  公共 ImmutableHolder(T newValue) { _Value = newValue; }
}

如果没有可变结构,这样的构造会更加尴尬。

于 2012-06-14T05:19:53.493 回答
5

从性能/内存使用的角度来看,这会更好吗?

不,结构是值类型,因此它们是按值复制的。这意味着您将始终复制整个对象,而不是仅复制一个引用。如果有的话,它可能会稍微慢一些并使用更多内存(因为对象将被复制)。

不应该这样做的另一个原因是:应始终避免使用可变结构,因为如果您不小心,它们可能会导致意外行为。

于 2012-06-13T12:12:28.370 回答
2

Astruct可能更危险的是,值类型的处理方式更加不同,CLR如果写得不好(例如可变结构),可能会让人头疼。此外,如果您的类型包含许多字段,然后从一个方法传递到另一个方法,那么您将在每个方法调用上复制每个字段的值,从而比您一开始使用类时使用更多的内存。

使用 DTO(数据传输对象)将鼓励状态和行为的分离。如果做得好,这可能是一个很好的设计(在大多数情况下,您将使用类而不是结构来实现此模式)。

此外,使用 astruct作为 the的具体情况DTO object似乎有缺陷,因为它引入了不必要redundancy的 . 因为structs cannot inherit other structs,你不能表达 is-a 关系。当你拥有Customerwhich 继承自Person. 你重复所有的Person properties in the Customer结构吗?您是否将 Person 结构嵌套在 Customer 结构中?这两种方法都不理想。如果您至少使用了类,则可以让 Customer 扩展 Person

于 2012-06-13T12:15:07.253 回答
1

您应该使用类方法,结构方法甚至可能会更慢,因为结构是值类型,并且无论您将它们作为函数参数传递到哪里,都会制作副本。因此,对于您的 DTO,请坚持使用课堂方法。

于 2012-06-13T12:16:36.807 回答
0

我的意见是:课程更好。

使用类,您可以实现 INotifyPropertyChanged 接口,如果您进行数据绑定,这将非常有帮助。

你也可以有一些私有字段,比如“ID”,只有“get”-property,你可以向你的类添加一些方法,你可以使你的实体可序列化。
我只想说,你对类更灵活,我认为在这种情况下结构和类之间没有很大的性能差异。

这是一本非常好的书,它通过设计 nTier 应用程序帮助了我很多:http: //msdn.microsoft.com/en-us/library/ee817644.aspx

看看那里。它说:“在结构和类之间进行选择。对于不包含分层数据或集合的简单业务实体,请考虑定义一个结构来表示业务实体。对于复杂的业务实体,或者对于需要继承的业务实体,将实体定义为而是一堂课。”

于 2012-06-13T12:44:13.557 回答