91

在元组之前,我曾经创建一个类及其变量,然后从这个类创建对象,并使该对象成为某些函数的返回类型。

现在,使用元组,我可以做同样的事情,在 C# 7.0 中,我们可以为元组属性分配易于理解的名称(在此之前,它是item1,item2等)

所以现在我想知道,我什么时候使用元组,什么时候在 C# 7.0 中创建一个类?

4

11 回答 11

66

由于这个答案在这里引起了一些人的困惑,我应该澄清一下——根据问题——这里所有对“元组”的引用都是指ValueTupleC# 7 的类型和新的元组语法糖特性,而不是指旧的System.Tuple引用类型。

所以现在我想知道,什么时候应该使用元组,什么时候应该在 c# 7.0 中创建一个类?

只有你才能真正回答这个问题,因为它真的取决于你的代码。

但是,您可以遵循一些指导方针和规则来指导您在它们之间进行选择:

元组是值,因此是按值复制的,而不是按引用复制的。

大多数时候,这不应该是一个问题。但是,如果您正在传递大型结构的元组,这可能会对性能产生影响。不过,可以使用 Ref locals/returns 来解决这些性能问题。

此外,由于它们是值,远程修改副本不会更改原始副本。这是一件好事,但可以抓住一些人。

元组元素名称不持久

给元素的名称由编译器使用,并且(在大多数情况下)在运行时不可用。这意味着不能使用反射来发现它们的名称;它们不能动态访问,也不能在 razor 视图中使用。

这也是 API 的一个重要考虑因素。从方法返回的元组是关于编译后名称可发现性规则的例外。编译器将属性添加到保存元组名称信息的方法中。这意味着您可以安全地从一个程序集中的公共方法返回一个元组,并在另一个程序集中访问它的名称。

元组是轻量级的

元组比类型更容易编写,因为它们不那么冗长,并且声明可以“内联”(即在使用点声明)。例如,这在声明返回多个值的方法时效果很好。

但是,因为它们是在使用时声明的,所以如果您有MethodA那个MethodB调用MethodC并且每个调用都返回一个元组,那么您需要在每个阶段重新定义元组。()没有一种方法可以创建元组的别名并在多个方法中重新使用它。

只需使用常识

对于您可能考虑使用元组的任何情况:只需问自己一个问题:“元组是否会简化此处的代码”。如果答案是“是”,则使用一个。这最终是使用元组还是自定义类的主要考虑因素。

于 2017-06-20T11:40:57.747 回答
32

一般来说,命名类在系统设计中具有一定的意义。它们也更冗长。例如,您可能有一个名为MediaFileOpener. 我们知道这个类做什么对设计很重要——我们正在处理媒体文件!

当没有设计意义并且您只需要一个轻量级的数据传输对象 (DTO) 来移动信息时,就会使用匿名类型和元组。

通常,如果您的类需要一些文档来描述它的用途,或者如果它提供了一些行为,请使用完整的类。如果您只需要临时存储或某种分组,请使用元组。考虑一种情况,您希望从异步方法返回多个值。Tuple 就是为了解决这个问题而设计的。

于 2017-06-20T10:54:14.213 回答
11

使用类

如果您的对象是在整个应用程序中广泛使用的实体,并且还存储在某种持久性存储中,例如关系数据库(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变量,但我相信元组更具表现力并提高了可读性。

于 2017-06-20T10:59:51.493 回答
6

通常,当您的对象将在其他地方使用时,或者如果它代表您领域中的真实对象或概念时,您希望拥有一个类。您可能会创建一个类来表示汽车或汽车商店,而不是元组。

另一方面,有时您只想从一个方法中返回几个对象。也许它们并不代表任何特别之处,只是您需要以该特定方法将它们一起返回。有时,即使它们确实代表了您域中的一个概念(例如,您正在返回(Car, Store),它可以表示为一个Sale对象),您实际上也不会在任何地方使用它们——您只是在移动数据。在这些情况下,可以使用元组。

现在,特别谈到 C#,您还应该知道一件事。C# 7 的元组类型实际上是ValueTuple,它是一个结构。与作为引用类型的类不同,结构是值类型。您可以在msdn上阅读更多相关信息。最重要的是,要知道它们可能涉及大量复制,所以要小心。

于 2017-06-20T11:00:01.797 回答
4

我认为这将成为一个经常被问到的问题。目前没有关于何时使用新值元组与类的“最佳实践”。

但是,值得一读之前关于元组与类的先前版本的对话中出现的内容

在我看来,值元组应该只使用最少并且最多不超过三个值。我认为这在“返回一些值而不需要类”和“可怕的价值观混乱”之间取得了很好的平衡。如果要返回的值超过三个,请创建一个类。

我也永远不会使用元组从消费者必须使用的面向公众的 API 返回。同样,只需使用一个类。

这是我使用过的一些真实世界的代码:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

一旦我想返回更复杂的数据,我就创建一个类。

于 2017-06-20T11:01:46.807 回答
4

我想首先提到 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和structapply之间的所有利弊。
  • ValueTuple可以显式使用(带或不带语法糖),使其具有多功能性,System.Tuple同时保留命名成员。
  • ValueTuple支持解构。

鉴于它必须是语法糖,我会说更强大的选择ValueTuple论点与选择 a 的论点相同struct。这对于主要存在于堆栈中的小型、不可变类型来说是理想的(所以你没有很多装箱和拆箱)。

ValueTuple完整的结构相比,考虑到语法糖,我建议ValueTuple默认使用,除非您需要显式布局或需要向其添加方法。

我还想说句法糖不一定能提高可读性。主要原因是您没有命名类型,而类型的名称为代码提供了意义。除此之外,您可以将文档添加到易于理解的structclass声明中。

总而言之,ValueTuple真正出色的情况是从一个方法返回多个值。在这种情况下,无需创建新out参数。并且使用的文档ValueTuple可以存在于方法的文档中。如果您发现需要使用ValueTuple(例如定义扩展方法)做其他事情,我建议您考虑创建一个命名类型。

于 2017-06-20T11:36:00.737 回答
3

我会避免使用元组作为公共方法的返回类型。在这种情况下,我更愿意定义一个类或结构。

于 2018-01-23T13:16:08.497 回答
2

元组旨在表示多个值,例如当方法打算返回多个值时。C# 7中的元组支持使用System.ValueTuple<...>实例来表示该组值。这些值的名称仅在使用它们的上下文中有效并且不强制执行。

类旨在表示具有多个属性的单个值。

于 2017-06-20T10:58:59.207 回答
1

当您想要将多个值(可以是不同的类型)组合到一个对象中而不创建自定义类时,元组是一个很好的选择。在这种情况下,Tuple 将是一个快速且完美的选择。

于 2018-03-15T10:43:25.333 回答
0

对于只使用一次的快速代码,请使用元组。如果必须维护代码,请使用类。查看代码并看到如下表达式令人沮丧:

if (t.Item4 == x.Item3)
于 2021-07-14T15:23:58.967 回答
0

我最近比较了一个案例的性能差异,该案例涉及一个包含 2 个(或 4 个)基元的元组和一个包含 2 个(或 4 个)相同基元类型的类。我在这里分享,以防万一可以帮助任何人。

剖析

于 2021-03-26T09:33:32.603 回答