System.ValueTuple和System.Tuple有什么区别? [英] What's the difference between System.ValueTuple and System.Tuple?

查看:486
本文介绍了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 代替?

A ValueTuple 是一种结构,它反映了元组,与原始的 System.Tuple 类相同。

元组 ValueTuple 之间的主要区别是:

The main difference between Tuple and ValueTuple are:


  • System.ValueTuple 是值类型(结构),而 System.Tuple 是引用类型( class )。在谈论分配和GC压力时,这很有意义。

  • System.ValueTuple 不仅是结构,它是一个 mutable ,因此在使用它们时必须要小心。想一想一个类将 System.ValueTuple 作为字段时会发生什么。

  • System.ValueTuple 通过字段而不是属性公开其项。

  • System.ValueTuple is a value type (struct), while System.Tuple is a reference type (class). This is meaningful when talking about allocations and GC pressure.
  • System.ValueTuple isn't only a struct, it's a mutable one, and one has to be careful when using them as such. Think what happens when a class holds a System.ValueTuple as a field.
  • System.ValueTuple exposes its items via fields instead of properties.

直到C#7,使用元组不是很方便。它们的字段名称是 Item1 Item2 等,并且该语言没有像其他大多数语言那样为它们提供语法糖

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.

此外,结构在运行时会获得自动(浅)等式语义,其中不。尽管设计团队确保为元组提供了更加优化的平等性,但因此为其实现了自定义相等性。

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


结构或类:



如上所述,我建议将元组类型设置为 structs 而不是
classes ,因此与它们无关的分配罚款。他们
应该尽可能轻巧。

Struct or Class:

As mentioned, I propose to make tuple types structs rather than classes, so that no allocation penalty is associated with them. They should be as lightweight as possible.

可以说,结构最终会变得更加昂贵,因为赋值
复制的值更大。因此,如果分配给它们的内容远远超过创建
的数目,那么结构将是一个糟糕的选择。

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有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆