何时使用:Tuple vs Class c#7.0

时间:2017-06-20 10:33:00

标签: c# tuples c#-7.0 valuetuple

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

现在有了元组,我可以做同样的事情,在c#7.0中,我们可以为元组属性分配可理解的名称(在此之前它是item1item2等。)

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

11 个答案:

答案 0 :(得分:33)

由于这个答案在这里引起了一些人的混淆,我应该澄清 - 按照这个问题 - 所有提及"元组"这里引用C#7的ValueTuple类型和新元组语法糖特性,绝不参考旧的System.Tuple引用类型。

  

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

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

但是,您可以遵循指导原则和规则来指导您选择它们​​:

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

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

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

元组名称不会保留

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

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

元组是轻量级的

元组的编写要比类型简单得多,因为它们不那么详细,而且声明可以是#34; inlined" (即在使用点宣布)。例如,在声明返回多个值的方法时,这很有效。

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

只需使用常识

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

答案 1 :(得分:18)

一般来说,命名类在系统设计中具有一定的意义。他们写作也比较冗长。例如,您可能有一个名为<p>To quickly input an invalid date, select the day field and press the down arrow. This will select 31/06/2017 and output the desired invalid date string.</p> <form><input type="date" value="2017-06-01"><output></output></form>的类。设计我们知道这个课程的作用非常重要 - 我们正在使用媒体文件!

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

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

答案 2 :(得分:8)

使用课程

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

答案 3 :(得分:3)

首先我要提到C#已经支持anonymous types。哪些是参考类型。因此,您已经有了创建命名类的合适替代方法。

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

当然,您可以使用System.Tuple来解决匿名类型的一些限制。这也是一种引用类型,您可以明确地使用它。缺点是缺乏成员的自定义名称。

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

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

第三个区别是ValueTuple支持开箱即用的解构。引用What’s New in C# 7.0

  

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

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

您还可以通过添加Deconstruct method来自定义类型。

抽象:

  • ValueTuple在C#7.0中具有语法糖,应考虑其可读性。
  • ValueTuple是值类型。使用classstruct之间的所有利弊都适用。
  • ValueTuple可以明确地使用(有或没有语法糖),允许它具有System.Tuple的多功能性,同时保留命名成员。
  • ValueTuple支持解构。

鉴于它必须是语法糖,我会说选择ValueTuple的更强有力的论据是选择struct的相同论点。这对于小型,不可变类型来说是理想的,它们主要存在于堆栈中(因此您没有大量的装箱和拆箱)。

比较ValueTuple与完整的结构,考虑到语法糖,我建议默认使用ValueTuple,除非您需要explicit layout或者需要添加方法

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

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

答案 4 :(得分:2)

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

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

答案 5 :(得分:2)

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

另一方面,有时您只想从方法中返回几个对象。也许他们不代表什么特别的东西,只是你需要用这种特殊方法将它们归还在一起。有时候,即使他们确实代表了您网域中的某个概念(比如说您正在返回(Car, Store),可以表示为Sale个对象),您实际上并不会使用他们在任何地方 - 你只是移动数据。在这些情况下,使用元组很好。

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

我认为这将成为一个被问到很多的问题。目前没有&#34;最佳做法&#34;何时使用新值元组与类。

但是,值得一读what came up during previous conversations about the previous versions of tuples vs a class

在我看来,值元组应该只使用最少且不超过最多三个值。我认为这可以很好地平衡&#34;返回一些值而不需要一个类&#34;和#34;可怕的价值观混乱&#34;。如果您要返回三个以上的值,请创建一个类。

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

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

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

只要我想返回更复杂的数据,我就会上课。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

Profiling

答案 10 :(得分:0)

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

if (t.Item4 == x.Item3)