What#39;s the difference between System.ValueTuple and System.Tuple?(System.ValueTuple 和 System.Tuple 有什么区别?)
问题描述
我反编译了一些 C# 7 库,发现使用了 ValueTuple
泛型.ValueTuples
是什么,为什么不是 Tuple
?
I decompiled some C# 7 libraries and saw ValueTuple
generics being used. What are ValueTuples
and why not Tuple
instead?
- https://docs.microsoft.com/en-gb/dotnet/api/system.tuple
- https://docs.microsoft.com/en-gb/dotnet/api/system.valuetuple
推荐答案
什么是
ValueTuples
,为什么不是Tuple
?
一个 ValueTuple
是一个反映元组的结构,与原始的 System.Tuple
类相同.
Tuple
和ValueTuple
的主要区别是:
System.ValueTuple
是值类型(struct),而System.Tuple
是引用类型(class
).这在谈论分配和 GC 压力时很有意义.System.ValueTuple
不仅是一个struct
,它还是一个可变,在使用它们时必须小心.想想当一个类将System.ValueTuple
作为一个字段时会发生什么.System.ValueTuple
通过字段而不是属性公开其项目.
System.ValueTuple
is a value type (struct), whileSystem.Tuple
is a reference type (class
). This is meaningful when talking about allocations and GC pressure.System.ValueTuple
isn't only astruct
, it's a mutable one, and one has to be careful when using them as such. Think what happens when a class holds aSystem.ValueTuple
as a field.System.ValueTuple
exposes its items via fields instead of properties.
在 C# 7 之前,使用元组并不是很方便.它们的字段名称是 Item1
、Item2
等,并且该语言没有像大多数其他语言(Python、Scala)那样为它们提供语法糖.
Until C# 7, using tuples wasn't very convenient. Their field names are Item1
, Item2
, etc, and the language hadn't supplied syntax sugar for them like most other languages do (Python, Scala).
当 .NET 语言设计团队决定在语言级别合并元组并为其添加语法糖时,一个重要因素是性能.使用 ValueTuple
作为值类型,您可以在使用它们时避免 GC 压力,因为(作为实现细节)它们将在堆栈上分配.
When the .NET language design team decided to incorporate tuples and add syntax sugar to them at the language level an important factor was performance. With ValueTuple
being a value type, you can avoid GC pressure when using them because (as an implementation detail) they'll be allocated on the stack.
此外,struct
会在运行时获得自动(浅)相等语义,而 class
则不会.尽管设计团队确保元组的相等性更加优化,因此为其实现了自定义相等性.
Additionally, a struct
gets automatic (shallow) equality semantics by the runtime, where a class
doesn't. Although the design team made sure there will be an even more optimized equality for tuples, hence implemented a custom equality for it.
这是Tuples
的设计说明中的一段>:
Here is a paragraph from the design notes of Tuples
:
如前所述,我建议创建元组类型 structs
而不是classes
,因此没有分配惩罚与它们相关联.他们应该尽可能轻量级.
Struct or Class:
As mentioned, I propose to make tuple types
structs
rather thanclasses
, so that no allocation penalty is associated with them. They should be as lightweight as possible.
可以说,structs
最终的成本可能更高,因为赋值复制更大的值.所以如果他们被分配的比他们多得多被创建,那么 structs
将是一个糟糕的选择.
Arguably, structs
can end up being more costly, because assignment
copies a bigger value. So if they are assigned a lot more than they
are created, then structs
would be a bad choice.
不过,就其动机而言,元组是短暂的.你会用当部分比整体更重要时.所以常见的模式将是构造,返回并立即解构他们.在这种情况下,结构显然更可取.
In their very motivation, though, tuples are ephemeral. You would use them when the parts are more important than the whole. So the common pattern would be to construct, return and immediately deconstruct them. In this situation structs are clearly preferable.
结构体还有许多其他好处,这将成为在下面很明显.
Structs also have a number of other benefits, which will become obvious in the following.
示例:
您可以很容易地看到使用 System.Tuple
很快就会变得模棱两可.例如,假设我们有一个计算 List<Int>
的总和和计数的方法:
Examples:
You can easily see that working with System.Tuple
becomes ambiguous very quickly. For example, say we have a method which calculates a sum and a count of a List<Int>
:
public Tuple<int, int> DoStuff(IEnumerable<int> values)
{
var sum = 0;
var count = 0;
foreach (var value in values) { sum += value; count++; }
return new Tuple(sum, count);
}
在接收端,我们最终得到:
On the receiving end, we end up with:
Tuple<int, int> result = DoStuff(Enumerable.Range(0, 10));
// What is Item1 and what is Item2?
// Which one is the sum and which is the count?
Console.WriteLine(result.Item1);
Console.WriteLine(result.Item2);
您可以将值元组解构为命名参数的方式是该功能的真正强大之处:
The way you can deconstruct value tuples into named arguments is the real power of the feature:
public (int sum, int count) DoStuff(IEnumerable<int> values)
{
var res = (sum: 0, count: 0);
foreach (var value in values) { res.sum += value; res.count++; }
return res;
}
在接收端:
var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");
或者:
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");
编译器好东西:
如果我们查看前面示例的封面,我们可以确切地看到当我们要求它解构时,编译器是如何解释 ValueTuple
的:
[return: TupleElementNames(new string[] {
"sum",
"count"
})]
public ValueTuple<int, int> DoStuff(IEnumerable<int> values)
{
ValueTuple<int, int> result;
result..ctor(0, 0);
foreach (int current in values)
{
result.Item1 += current;
result.Item2++;
}
return result;
}
public void Foo()
{
ValueTuple<int, int> expr_0E = this.DoStuff(Enumerable.Range(0, 10));
int item = expr_0E.Item1;
int arg_1A_0 = expr_0E.Item2;
}
在内部,编译后的代码使用 Item1
和 Item2
,但由于我们使用的是分解的元组,所有这些都从我们身上抽象出来.带有命名参数的元组使用 TupleElementNamesAttribute
.如果我们使用单个新变量而不是分解,我们会得到:
Internally, the compiled code utilizes Item1
and Item2
, but all of this is abstracted away from us since we work with a decomposed tuple. A tuple with named arguments gets annotated with the TupleElementNamesAttribute
. If we use a single fresh variable instead of decomposing, we get:
public void Foo()
{
ValueTuple<int, int> valueTuple = this.DoStuff(Enumerable.Range(0, 10));
Console.WriteLine(string.Format("Sum: {0}, Count: {1})", valueTuple.Item1, valueTuple.Item2));
}
请注意,当我们调试我们的应用程序时,编译器仍然必须(通过属性)发生一些神奇的事情,因为看到 Item1
、Item2
会很奇怪.
Note that the compiler still has to make some magic happen (via the attribute) when we debug our application, as it would be odd to see Item1
, Item2
.
这篇关于System.ValueTuple 和 System.Tuple 有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:System.ValueTuple 和 System.Tuple 有什么区别?


- WebMatrix WebSecurity PasswordSalt 2022-01-01
- 如何用自己压缩一个 IEnumerable 2022-01-01
- 在哪里可以找到使用中的C#/XML文档注释的好例子? 2022-01-01
- C# 中多线程网络服务器的模式 2022-01-01
- 带有服务/守护程序应用程序的 Microsoft Graph CSharp SDK 和 OneDrive for Business - 配额方面返回 null 2022-01-01
- 输入按键事件处理程序 2022-01-01
- C#MongoDB使用Builders查找派生对象 2022-09-04
- MoreLinq maxBy vs LINQ max + where 2022-01-01
- 良好实践:如何重用 .csproj 和 .sln 文件来为 CI 创建 2022-01-01
- Web Api 中的 Swagger .netcore 3.1,使用 swagger UI 设置日期时间格式 2022-01-01