DTO.属性或字段

本文关键字:字段 属性 DTO | 更新日期: 2023-09-27 18:35:47

我需要创建一些 DTO 类来跨 WCF 传输我们的业务对象。

由于这些只是没有功能的数据包,我有什么理由不能只使用字段,或者是否有一些很好的理由将它们作为属性正确公开?

//fields
[DataContract]
class CustomerDTO
{
    [DataMember] public int     Id;
    [DataMember] public string  Name;
}
//or properties?
[DataContract]
class CustomerDTO
{
    [DataMember] public int    Id               { get; set; }
    [DataMember] public string Name             { get; set; }
}

DTO.属性或字段

如果我能侥幸逃脱的话,我最喜欢带有只读字段的不可变 DTO:

public class CustomerDTO
{
    public CustomerDTO(int id, string name)
    {
        Id = id;
        Name = name;
    }
    public readonly int     Id;
    public readonly string  Name;
    // Override Equals and GetHashCode as well...
}

不可变记录有很多优点,例如结构相等,这使得自动测试断言的编写变得更加简单。它还无需编写和维护单独的测试数据构建器。

不过,这取决于序列化程序。 JSON.NET 可以处理不可变记录,但许多其他序列化程序不能。

对于那些处理公共字段的人,我更喜欢字段而不是属性,仅仅是因为它更诚实;自动实现的读/写属性不提供封装。

某些序列化程序坚持使用公共属性,并且不序列化字段。如果是这种情况,你必须接受它。

老实说,考虑到我为此付出了多少心思,这并不是真正让我夜不能寐的东西,因为最终,在边界上,应用程序不是面向对象的。因此,OOD的规则无论如何都不适用于DTO。

由于这些只是没有功能的数据包,我有什么理由不能只使用字段

这里没有反对公共领域的有力论据。但要意识到,这只是因为 DTO 内部没有逻辑(行为),因此封装的正常论点不成立。

我仍然更喜欢属性,但它们在这里并不是真正必要的。

你可以使用任何一个。 由于它不会影响性能,因此在遇到某些序列化框架或不适用于公共字段的类似框架时,使用属性会更安全。

请注意,WCF 代理生成将在客户端使用公共属性及其后备私有字段创建这些 DTO,即使您在服务端使用公共字段也是如此。如果您不想要这样,则需要在服务和客户端之间共享 DTO 库。

DataMember 属性将同时适用于公共字段和属性,因此两者都是可能的。但是,我建议坚持使用属性。

特别是,如果您使用的是StyleCop,那么您将违反规则SA1401。

此规则存在的原因并不真正适用于您的情况,但如果您在持续集成服务器上将 StyleCop 验证作为构建的一部分运行,它仍然是一个维护问题。

以下是属性相对于字段的两个优点,我没有看到其他人提到。

  1. 语法中的摩擦与功能正确匹配

我认为默认行为应该是更安全的选择。 对于字段,默认行为允许可变性,这是更危险的选项。 必须添加关键字 readonly 才能使字段不可变(在构造函数之外)。 相反,auto 属性的默认状态是不可变的;必须添加语法set;,属性才能可变(在构造函数外部)。

  1. Visual Studio 的 Code Lens 中给出的引用计数

与字段不同,在Visual Studio中查看属性具有"Code Lens",其中包括其引用计数。 由于这些附加信息,我能够比字段更快地理解和重构属性。 当然,可以使用 Shift+F12 在 Visual Studio 中搜索对字段和属性等内容的引用,但这是一个需要时间的额外步骤。 此外,在极少数情况下,一些项目包括多个目标。 这使得 Shift+F12 搜索结果更难使用,因为对每个目标都执行搜索。 在合同中,属性代码 Len 中给出的引用计数与项目目标的数量无关。

结论

不过,为了直接回答这个问题,我更喜欢使用可变属性(并且没有显式构造函数),以确保我将在序列化库中使用最快乐的快乐路径(在@ErenErsönmez的答案中提到过)。

我永远不会直接公开字段,大多数公司在其标准中禁止这样做。实际上,您完全抛弃了封装。DTO,作为更复杂事物的贫乏表示是一个奇怪的情况,因为它们的属性无论如何几乎都会破坏封装。就个人而言,我会使用这些属性,因为这就是它们的目的。如果需要,它还允许您实现"脏"功能等,如果您直接调整字段,这并不容易。