本文转载自微信公众号“DotNET技术圈”,作者ArdalisSteve。转载本文请联系DotNET技术圈公众号。在讨论.NET和C#软件开发时经常出现的两个术语是DTO和POCO。一些开发人员可以互换使用这些术语。那么,DTO和POCO之间有什么区别?首先,让我们定义每个术语。数据传输对象(DTO)DTO是“数据传输对象”。它是一个对象,其目的是传输数据。根据定义,DTO应该只包含数据,而不是逻辑或行为。如果DTO包含逻辑,则它不是DTO。但是等等,什么是“逻辑”或“行为”?通常,逻辑和行为指的是类型上的方法。在C#中,DTO应该只有属性,而这些属性应该只获取和设置数据,而不是对其进行验证或执行其他操作。属性和数据注释呢?将元数据添加到DTO以使其支持模型验证或类似目的的情况并不少见。这些属性不会向DTO本身添加任何行为,而是促进系统中其他地方的行为。所以他们没有违反DTO不应包含任何行为的“规则”。ViewModel、API模型等呢?DTO一词非常模糊。它只是说一个对象只包含数据,不包含行为。它没有说明其预期用途。在许多体系结构中,DTO可以充当多个角色。例如,在大多数具有支持绑定到数据类型的视图的MVC体系结构中,DTO用于将数据传递和绑定到视图。这些DTO通常称为ViewModel,理想情况下,除了按照View的预期格式化数据外,它们应该没有其他行为。所以在这种情况下,ViewModel是一种特定类型的DTO。但是,请小心。那么你不能断定所有的ViewModel都是DTO,因为在MVVM架构[1]中,ViewModel通常包含很多行为。因此,在做出任何广泛的假设之前考虑上下文是很重要的。即使在MVC应用程序中,有时也会将逻辑添加到ViewModel,这样它们就不再是DTO。DTO和ViewModel尽可能根据预期用途命名DTO。将类命名为FooDTO并没有说明该类型在应用程序体系结构中的使用方式或位置。相反,更喜欢FooViewModel.C#中的示例DTO下面是C#中的示例DTO对象:}publicdecimalUnitPrice{get;set;}}封装和数据传输对象封装是面向对象设计的一个重要原则。但它不适用于DTO。封装用于防止类的协作者过于依赖有关类如何执行其操作或存储其数据的特定实现细节。由于DTO没有操作或行为,并且不应该有隐藏状态,因此它们不需要封装。不要通过使用私有setter或试图使您的DTO表现得像不可变值对象来让您的生活变得更艰难。您的DTO应该易于创建、编写和阅读。他们应该支持序列化,而无需任何自定义工作来支持它。字段或属性既然DTO不关心封装,为什么要使用属性呢?为什么不只是字段?您可以使用其中任何一种,但某些序列化框架仅适用于属性。我通常使用属性,因为这是C#中的约定,但如果您更喜欢公共字段或有设计原因为什么它们更可取,您当然可以使用它们。无论您选择哪种方式,在您的应用程序中使用字段或属性时,我都会尽量保持一致。这里有一些优缺点的讨论[3]。不变性和记录类型不变性在软件开发中有很多好处,在DTO中也是一个有用的特性。JimmyBogard写过试图在DTO中实现不变性[4],而MarkSeeman[5]在对那篇文章的评论(以及上面的StackOverflow问题)中采用了相反的方法。就个人而言,我通常不会将DTO构建为不可变的,正如您从上面显示的示例中看到的那样。然而,这可能会随着C#9及其记录类型的引入而改变[6]。顺便说一下,您可能会看到的另一个首字母缩写词是数据传输记录或DTR。下面是使用C#9定义DTR的一种方法:publicrecordProductDTO(intId,stringName,stringDescription);当使用记录类型和上述位置进行声明时,将按照与声明相同的顺序为您生成一个构造函数。因此,您将使用以下语法创建此DTR:vardto=newProductDTO(1,"devBetterMembership","Aone-yearsubscriptiontodevBetter.com");或者,您可以以更传统的方式定义属性并在构造函数中设置它们。另一个新特性是init-only属性,它支持在创建时初始化,但在其他情况下是只读的,保持记录不可变。一个例子:publicrecordProductDTO{publicintId{get;init;}publicstringName{get;init;}}//usagevardto=newProductDTO{Id=1,Name="somename"};C#记录类型在使用位置声明时不需要任何特殊的努力,即支持序列化。如果您创建自己的自定义构造函数,您可能需要向序列化程序提供一些提示。随着C#9、.NET5和记录类型变得越来越流行,我希望更频繁地将它们用于DTR。普通旧CLR对象或普通旧C#对象(POCO)普通旧CLR/C#对象是POCO。Java有普通的旧Java对象,或POJO。您真的可以将这些统称为“普通旧对象”,但我猜有人不喜欢由此产生的首字母缩略词。那么,对象“陈旧”意味着什么?基本上,它不依赖于特定的框架或库来运行。一个普通的旧对象可以在您的应用程序或测试中的任何地方实例化,并且不需要涉及特定的数据库或第三方框架来运行。通过显示反例来演示POCO是最简单的。下面的类依赖于一些引用数据库的静态方法,使得该类完全依赖于数据库的存在才能发挥作用。它还继承自第三方持久性框架(由其组成)中定义的类型。publicclassProduct:DataObject
