何时使用:Tuple vs Class c#7.0

Mad*_*mon 55 c# tuples c#-7.0 valuetuple

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

现在用的元组,我可以做同样的在C#7.0,我们可以将元组性质可以理解的名称(这是之前item1,item2等..)

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

Dav*_*rno 40

由于这个答案在这里引起了一些人的混淆,我应该澄清 - 根据问题 - 这里所有对"元组"的引用都是指ValueTupleC#7 的类型和新元组语法糖特征,绝不是指旧的System.Tuple参考类型.

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

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

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

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

大多数时候,这应该不是问题.但是,如果您传递大型结构的元组,这可能会对性能产生影响.但是,参考本地/返回可用于解决这些性能问题.

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

元组名称不会保留

编译器使用赋予元素的名称,并且(在大多数情况下)在运行时不可用.这意味着反射不能用于发现他们的名字; 它们无法动态访问,也无法在剃刀视图中使用.

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

元组很轻巧

编写比类型更简单,因为它们不那么详细,并且声明可以"内联"(即在使用时声明).例如,在声明返回多个值的方法时,这很有效.

但是,因为它们是在使用点声明的,如果你有MethodA调用的MethodB调用MethodC并且每个调用返回一个元组,你需要在每个阶段重新定义元组.没有()创建一个元组的别名,并重新使用它跨多个方法的方法.

只需使用常识

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

  • `ValueTuple` 是值类型。`Tuple` 是引用类型。如此简单的 `Tuple` 不会被引用复制。你应该用粗体提到它,因为它现在很混乱 (2认同)
  • @Szer,问题是关于C#7的元组的,所以我不明白为什么会有任何混乱。但我已经更新了问题以澄清这一点。 (2认同)

Gus*_*dor 20

一般来说,命名类在系统设计中具有一定的意义.他们写作也比较冗长.例如,您可能有一个名为的类MediaFileOpener.设计我们知道这个课程的作用非常重要 - 我们正在使用媒体文件!

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

按规则,如果您的类需要一些文档来描述它的用途,或者是否存在它提供的行为,请使用完整的类.如果您只需要临时存储或某种分组,请使用元组.考虑一种情况,您希望从异步方法返回多个值.元组旨在解决这个问题.


dim*_*cas 8

使用班级

如果您的对象是在整个应用程序中广泛使用的实体,并且还存储在某种持久存储中,如关系数据库(SQL Server,MySQL,SQLite),NoSQL数据库或缓存(Redis,Azure DocumentDB),甚至是简单的文本文件或CSV.

所以,任何持久的东西都应该有自己的类.

使用元组

如果您的对象是短暂的,对您的应用没有特殊意义.例如,如果你需要快速返回一对坐标,最好有这样的东西:

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}
Run Code Online (Sandbox Code Playgroud)

而不是定义一个单独的类

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

new对于这样一个简单的操作,元组将节省您在堆上分配内存的时间.

另一个我发现元组有用的时候是对某些操作数执行多个数学运算

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)
Run Code Online (Sandbox Code Playgroud)

在这种情况下定义一个类是没有意义的.无论计算结果如何,结果都不属于某一类.所以替代方案是使用out变量,但我相信元组更具表现力,并提高可读性.

  • 坐标将是不使用元组的一个很好的例子,因为它是一个常见的东西,你可能一直需要**.另外,你可以重载`ToString`以提供可读的输出(顺便提一下,元组的默认ToString在这里已经运行良好),并且重载`Equals` /`GetHashCode`以进行相等比较. (5认同)
  • 我将`Coordinates`定义为`struct`而不是`class`. (2认同)

and*_*ss6 6

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

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

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


The*_*aot 6

我想首先提到 C# 已经支持匿名类型。哪些是引用类型。因此,您已经有了一个合适的替代方案来创建命名类。

\n

命名类的优点之一是更容易重用(例如,如果您在多个位置需要相同的类型)和文档。由于匿名类型是匿名的,因此只有在可以使用的情况下才能获取输入到它的变量var,则只能获取键入的变量,这限制了匿名类型有用的上下文(例如,不能将它们用作字段类型、返回类型或参数类型) )。

\n

当然,您可以通过使用来克服匿名类型的一些限制System.Tuple。这也是一个引用类型,您可以显式地使用它。缺点是它缺乏成员的自定义名称。

\n
\n

C# 7 元组 (ValueTuple ) 可以被视为类似于匿名类型。第一个区别是它们是值类型。这意味着这些元组只要保留在本地范围内或在堆栈中移动(由于其限制,这是匿名类型的常见用法),它们就会具有性能优势。

\n

第二个区别是,新语法允许元组出现在比匿名类型更多的地方,如您所知,您有语法糖来定义返回类型ValueTuple(当使用匿名类型时,您必须返回object)。

\n

第三个区别是ValueTuple支持开箱即用的解构。引用C# 7.0 中的新功能 What\xe2\x80\x99s

\n
\n

使用元组的另一种方法是解构它们。解构声明是一种将元组(或其他值)拆分为多个部分并将这些部分单独分配给新变量的语法:

\n
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration\nWriteLine($"found {first} {last}.");\n
Run Code Online (Sandbox Code Playgroud)\n
\n

您还可以通过添加Deconstruct 方法来处理自定义类型

\n
\n

对于摘要:

\n
    \n
  • ValueTupleC# 7.0 中有语法糖,应考虑可读性。
  • \n
  • ValueTuple是一个值类型。使用 aclass和 a之间的所有优点和缺点struct之间的所有优点和缺点都适用。
  • \n
  • ValueTuple可以明确使用(有或没有语法糖),使其具有以下多功能性System.Tuple同时保留命名成员。
  • \n
  • ValueTuple支持解构。
  • \n
\n

鉴于它必须是语法糖,我想说选择的更强论据ValueTuple与选择一个的论据相同struct。这对于小型、不可变的类型来说是理想的选择,这些类型主要存在于堆栈中(因此您不需要进行大量的装箱和拆箱)。

\n

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

\n

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

\n

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

\n


use*_*993 5

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

然而,值得阅读之前关于元组与类的先前版本的对话中出现的内容

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

我也永远不会使用元组从消费者必须使用的面向公众的 API 返回。再次强调,只需使用一个类即可。

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

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()
Run Code Online (Sandbox Code Playgroud)

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