在元组之前,我曾经创建一个类及其变量,然后从这个类创建对象,并使该对象成为某些函数的返回类型。
现在,使用元组,我可以做同样的事情,在 C# 7.0 中,我们可以为元组属性分配易于理解的名称(在此之前,它是item1
,item2
等)
所以现在我想知道,我什么时候使用元组,什么时候在 C# 7.0 中创建一个类?
在元组之前,我曾经创建一个类及其变量,然后从这个类创建对象,并使该对象成为某些函数的返回类型。
现在,使用元组,我可以做同样的事情,在 C# 7.0 中,我们可以为元组属性分配易于理解的名称(在此之前,它是item1
,item2
等)
所以现在我想知道,我什么时候使用元组,什么时候在 C# 7.0 中创建一个类?
由于这个答案在这里引起了一些人的困惑,我应该澄清一下——根据问题——这里所有对“元组”的引用都是指ValueTuple
C# 7 的类型和新的元组语法糖特性,而不是指旧的System.Tuple
引用类型。
所以现在我想知道,什么时候应该使用元组,什么时候应该在 c# 7.0 中创建一个类?
只有你才能真正回答这个问题,因为它真的取决于你的代码。
但是,您可以遵循一些指导方针和规则来指导您在它们之间进行选择:
大多数时候,这不应该是一个问题。但是,如果您正在传递大型结构的元组,这可能会对性能产生影响。不过,可以使用 Ref locals/returns 来解决这些性能问题。
此外,由于它们是值,远程修改副本不会更改原始副本。这是一件好事,但可以抓住一些人。
给元素的名称由编译器使用,并且(在大多数情况下)在运行时不可用。这意味着不能使用反射来发现它们的名称;它们不能动态访问,也不能在 razor 视图中使用。
这也是 API 的一个重要考虑因素。从方法返回的元组是关于编译后名称可发现性规则的例外。编译器将属性添加到保存元组名称信息的方法中。这意味着您可以安全地从一个程序集中的公共方法返回一个元组,并在另一个程序集中访问它的名称。
元组比类型更容易编写,因为它们不那么冗长,并且声明可以“内联”(即在使用点声明)。例如,这在声明返回多个值的方法时效果很好。
但是,因为它们是在使用时声明的,所以如果您有MethodA
那个MethodB
调用MethodC
并且每个调用都返回一个元组,那么您需要在每个阶段重新定义元组。(还)没有一种方法可以创建元组的别名并在多个方法中重新使用它。
对于您可能考虑使用元组的任何情况:只需问自己一个问题:“元组是否会简化此处的代码”。如果答案是“是”,则使用一个。这最终是使用元组还是自定义类的主要考虑因素。
一般来说,命名类在系统设计中具有一定的意义。它们也更冗长。例如,您可能有一个名为MediaFileOpener
. 我们知道这个类做什么对设计很重要——我们正在处理媒体文件!
当没有设计意义并且您只需要一个轻量级的数据传输对象 (DTO) 来移动信息时,就会使用匿名类型和元组。
通常,如果您的类需要一些文档来描述它的用途,或者如果它提供了一些行为,请使用完整的类。如果您只需要临时存储或某种分组,请使用元组。考虑一种情况,您希望从异步方法返回多个值。Tuple 就是为了解决这个问题而设计的。
使用类
如果您的对象是在整个应用程序中广泛使用的实体,并且还存储在某种持久性存储中,例如关系数据库(SQL Server、MySQL、SQLite)、NoSQL 数据库或缓存(Redis、Azure DocumentDB),甚至是简单的文本文件或 CSV。
所以,是的,任何持久的东西都应该有自己的类。
使用元组
如果您的对象是短暂的,对您的应用程序没有特殊意义。例如,如果您需要快速返回一对坐标,最好有这样的东西:
(double Latitude, double Longitude) getCoordinates()
{
return (144.93525, -98.356346);
}
而不是定义一个单独的类
class Coordinates
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
元组将节省您在堆上分配内存的时间,new
用于这样一个简单的操作。
另一个我发现元组有用的时候是对某些操作数执行多个数学运算时
(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)
在这种情况下定义一个类是没有意义的。不管是什么计算的结果都不属于一个类。因此,替代方法是使用out
变量,但我相信元组更具表现力并提高了可读性。
通常,当您的对象将在其他地方使用时,或者如果它代表您领域中的真实对象或概念时,您希望拥有一个类。您可能会创建一个类来表示汽车或汽车商店,而不是元组。
另一方面,有时您只想从一个方法中返回几个对象。也许它们并不代表任何特别之处,只是您需要以该特定方法将它们一起返回。有时,即使它们确实代表了您域中的一个概念(例如,您正在返回(Car, Store)
,它可以表示为一个Sale
对象),您实际上也不会在任何地方使用它们——您只是在移动数据。在这些情况下,可以使用元组。
现在,特别谈到 C#,您还应该知道一件事。C# 7 的元组类型实际上是ValueTuple
,它是一个结构。与作为引用类型的类不同,结构是值类型。您可以在msdn上阅读更多相关信息。最重要的是,要知道它们可能涉及大量复制,所以要小心。
我认为这将成为一个经常被问到的问题。目前没有关于何时使用新值元组与类的“最佳实践”。
但是,值得一读之前关于元组与类的先前版本的对话中出现的内容。
在我看来,值元组应该只使用最少并且最多不超过三个值。我认为这在“返回一些值而不需要类”和“可怕的价值观混乱”之间取得了很好的平衡。如果要返回的值超过三个,请创建一个类。
我也永远不会使用元组从消费者必须使用的面向公众的 API 返回。同样,只需使用一个类。
这是我使用过的一些真实世界的代码:
public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()
一旦我想返回更复杂的数据,我就创建一个类。
我想首先提到 C# 已经支持匿名类型。哪些是引用类型。因此,您已经有了一个合适的替代方法来创建命名类。
命名类的一个优点是更容易重用(例如,如果您在多个地方需要相同的类型)和文档。由于匿名类型是匿名的,所以只有可以使用的情况下才能获取键入给它的变量var
,这限制了匿名类型有用的上下文(例如,不能将它们用作字段类型、返回类型或参数类型) .
当然,您可以使用System.Tuple
. 这也是一种引用类型,您可以显式使用它。缺点是它缺少成员的自定义名称。
C# 7 元组 ( ValueTuple
) 可以被认为类似于匿名类型。第一个区别是它们是值类型。这意味着只要这些元组保持在本地范围内或在堆栈中移动(这是匿名类型的常见用法,由于其局限性),它们就会具有性能优势。
第二个区别是新语法允许元组出现在比匿名类型更多的地方,正如你所知,你有语法糖来定义返回类型ValueTuple
(当使用匿名类型时你必须返回object
)。
第三个区别是ValueTuple
支持开箱即用的解构。引用C# 7.0 中的新增功能:
使用元组的另一种方法是解构它们。解构声明是一种将元组(或其他值)拆分为其部分并将这些部分单独分配给新变量的语法:
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration WriteLine($"found {first} {last}.");
您还可以通过添加Deconstruct 方法来处理自定义类型。
摘要:
ValueTuple
在 C# 7.0 中有语法糖,应考虑到可读性。ValueTuple
是一个值类型。class
使用 a和struct
apply之间的所有利弊。ValueTuple
可以显式使用(带或不带语法糖),使其具有多功能性,System.Tuple
同时保留命名成员。ValueTuple
支持解构。鉴于它必须是语法糖,我会说更强大的选择ValueTuple
论点与选择 a 的论点相同struct
。这对于主要存在于堆栈中的小型、不可变类型来说是理想的(所以你没有很多装箱和拆箱)。
与ValueTuple
完整的结构相比,考虑到语法糖,我建议ValueTuple
默认使用,除非您需要显式布局或需要向其添加方法。
我还想说句法糖不一定能提高可读性。主要原因是您没有命名类型,而类型的名称为代码提供了意义。除此之外,您可以将文档添加到易于理解的struct
或class
声明中。
总而言之,ValueTuple
真正出色的情况是从一个方法返回多个值。在这种情况下,无需创建新out
参数。并且使用的文档ValueTuple
可以存在于方法的文档中。如果您发现需要使用ValueTuple
(例如定义扩展方法)做其他事情,我建议您考虑创建一个命名类型。
我会避免使用元组作为公共方法的返回类型。在这种情况下,我更愿意定义一个类或结构。
元组旨在表示多个值,例如当方法打算返回多个值时。C# 7中的元组支持使用System.ValueTuple<...>
实例来表示该组值。这些值的名称仅在使用它们的上下文中有效并且不强制执行。
类旨在表示具有多个属性的单个值。
当您想要将多个值(可以是不同的类型)组合到一个对象中而不创建自定义类时,元组是一个很好的选择。在这种情况下,Tuple 将是一个快速且完美的选择。
对于只使用一次的快速代码,请使用元组。如果必须维护代码,请使用类。查看代码并看到如下表达式令人沮丧:
if (t.Item4 == x.Item3)