DTO课程与结构

时间:2012-06-13 12:07:10

标签: c# struct dto n-tier-architecture

所以,实际上这个问题是我目前的基石。 我正在努力重构我的个人项目,尝试提高性能,优化内存使用,使代码简单明了。我有一个不同的应用程序层(实际上,DAL,BLL,ServiceAgent是WCF服务)。我使用实体/模型/ DTO在这些层之间传递数据,这是无状态的(根本没有任何逻辑)。目前这是一个像这样的对象

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

我曾经使用这种方法,它就像是“最佳实践”,是吗?这是存储传输数据的最佳方式吗?如果我为此更改 struct 该怎么办:

public struct Peson
{
    public ComplexIdentifier ComplexId;
    public string Name;
}
public struct ComplexIdentifier
{
    public int LocalId;
    public int GlobalId;
}

从性能/内存使用角度来看,这会更好吗?或者也许有某种陷阱这样做?

6 个答案:

答案 0 :(得分:16)

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

struct具有比类更多有限范围的潜在用例。 struct类型过大时也存在效率问题(不要忘记,它们是值类型,并且在传递时复制),如{{3}中所述}。当您开始通过属性公开struct类型时,或者在引用接口时意外地将其封装,或者使它们变得可变时,更不用说充足的陷阱......

我不是MSDN guidelines about value types struct当它相关时,但我非常很少发现自己需要在我们的主桌面中使用struct类型应用程序 - 分层并具有DTO类型。

<小时/> 无法像structclass那样简单地回答性能问题。您需要使用saying not to usedotTrace等分析工具才能找到热点并从那里开始。性能问题并非微不足道,好的工具通常是答案的开始。

答案 1 :(得分:5)

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

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

  SomeReadonlyStruct.SomeProperty = 5;

将由编译器静默转换为:

  var temp = SomeReadonlyStruct;
  temp.SomeProperty = 5;

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

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

请考虑以下代码:

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

有些人不喜欢这样一个事实:如果customerPhone是一个公开的字段结构,设置Extension属性不会影响someDataSource中的信息。虽然写一个struct字段不会更新任何其他东西,但这是一个比customerPhone是一个可变类类型时更好的情况。任何了解customerPhone是暴露字段结构类型的人都会知道对其成员的更改不会影响其他任何内容。相反,如果customerPhone是可变类类型,则上述代码可能会通过更改与someDataSource关联的电话号码来更新customerID。或者它可能不会。或者,如果一个电话号码与两个customerID值相关联,则上述代码可能会更改这两个值。可能需要研究的代码量以确定上述代码可能具有的确切影响和副作用将是非常大的。更糟糕的是,可能很难确定一个人没有错过任何东西。

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

interface IReadableHolder<T> { T Value {get;} }

class MutableHolder<T> : IReadableHolder<T>
{
  public T Value;
  IReadableHolder.Value {get {return Value;} }
  public MutableHolder(T newValue) { Value = newValue; }
  public MutableHolder(IReadableHolder<T> it) { Value = it.Value; }
}
class ImmutableHolder<T> : IReadableHolder<T>
{
  T _Value;
  public Value {get {return _Value;} }
  public ImmutableHolder(T newValue) { _Value = newValue; }
}

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

答案 2 :(得分:4)

  

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

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

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

答案 3 :(得分:2)

struct可能更危险的是CLR处理的值类型更加不同,如果写得不好(例如可变结构)会造成可怕的麻烦。 此外,如果您的类型包含许多字段,然后从方法传递到方法,您将在每个方法调用上复制每个字段的值,从而使用比首先使用类更多的内存。

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

此外,使用struct作为DTO object的具体情况似乎存在缺陷,因为它引入了不必要的redundancy。从structs cannot inherit other structs起,你无法表达is-a关系。如果Customer继承自Person,您会怎么做?你重复所有的Person properties in the Customer结构吗?您是否在Customer结构中嵌入Person结构?这两种方法都不理想。如果您至少使用过课程,您可以让客户扩展人员

答案 4 :(得分:1)

你应该使用类方法,结构方法甚至可能会更慢,因为结构是值类型,并且只要你将它们作为函数参数传递,就会产生副本。因此,对于你的DTO坚持类aproach。

答案 5 :(得分:0)

我的观点是:课程更好。

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

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

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

看看那里。它说: “在结构和类之间进行选择。对于没有结构和类的简单业务实体 包含分层数据或集合,考虑定义要表示的结构 商业实体。对于复杂的业务实体,或对于业务实体 要求继承,将实体定义为类。“