What is the difference between System.ValueTuple and System.Tuple?

I decompiled some C # 7 libraries and saw ValueTuple generics. What are ValueTuples and why not Tuple instead?

+109
c # tuples
Dec 11 '16 at 8:48
source share
7 answers

What are ValueTuples and why not Tuple ?

ValueTuple is a structure that reflects a tuple, just like the original System.Tuple class.

The main difference between Tuple and ValueTuple :

  • System.ValueTuple is the value type (structure), and System.Tuple is the reference type ( class ). This makes sense when it comes to resource allocation and GC pressure.
  • System.ValueTuple is not only a struct , but also a mutable struct , so be careful when using them as such. Think about what happens when a class contains System.ValueTuple as a field.
  • System.ValueTuple exposes its elements through fields instead of properties.

Prior to C # 7, using tuples was not very convenient. Their field names are Item1 , Item2 , etc., And the language did not provide them with syntactic sugar, as most other languages ​​do (Python, Scala).

When the .NET team decided to include tuples and add syntactic sugar to them at the language level, performance was an important factor. Since ValueTuple is a type of value, you can avoid the GC pressure when using them, because (as implementation details) they will be placed on the stack.

In addition, the struct gets automatic (surface) semantics of equality at runtime, but class does not. Although the development team made sure that the tuples had even more optimized equality, therefore, they realized their own equality for it.

Here is a paragraph from Tuples design Tuples :

Structure or class:

As already mentioned, I suggest doing structs of tuple types, not classes , so that they do not have a distribution penalty associated with them. They should be as light as possible.

Perhaps structs may turn out to be more expensive because assignment copies more value. So if they are assigned much more than they are created, then structs will be a bad choice.

However, by its very motivation, the tuples are ephemeral. You will use them when parts are more important than the whole. Thus, the general scheme will be the creation, return, and immediate deconstruction. In this situation, structures are clearly preferable.

Structures also have a number of other advantages that will become apparent in the following.

Examples:

You can easily see that working with System.Tuple very quickly becomes ambiguous. For example, let's say we have a method that calculates the sum and quantity 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); } 

At the receiving end we get:

 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 tuples of values ​​into named arguments is the real strength of the function:

 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; } 

And at the receiving end:

 var result = DoStuff(Enumerable.Range(0, 10)); Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}"); 

Or:

 var (sum, count) = DoStuff(Enumerable.Range(0, 10)); Console.WriteLine($"Sum: {sum}, Count: {count}"); 

The usefulness of the compiler:

If we look under the cover of our previous example, we will see exactly how the compiler interprets ValueTuple when we ask to deconstruct it:

 [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; } 

Internally, the compiled code uses Item1 and Item2 , but all of this is abstracted from us, since we are working with a decomposed tuple. A tuple with named arguments is annotated by TupleElementNamesAttribute . If instead of decomposing we use one fresh variable, 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)); } 

Please note that when debugging our application, the compiler should still do some magic (via the attribute), since it would be strange to see Item1 , Item2 .

+166
Dec 11 '16 at 8:56
source share

The difference between Tuple and ValueTuple is that Tuple is a reference type and ValueTuple is a value type. The latter is desirable because changes in the language in C # 7 have tuples that are used much more often, but allocating a new object on the heap for each tuple is a performance issue, especially when this is not necessary.

However, in C # 7, the idea is that you never need to explicitly use any type due to the addition of syntactic sugar to use the tuple. For example, in C # 6, if you want to use a tuple to return a value, you will need to do the following:

 public Tuple<string, int> GetValues() { // ... return new Tuple(stringVal, intVal); } var value = GetValues(); string s = value.Item1; 

However, in C # 7 you can use this:

 public (string, int) GetValues() { // ... return (stringVal, intVal); } var value = GetValues(); string s = value.Item1; 

You can even go further and name the values:

 public (string S, int I) GetValues() { // ... return (stringVal, intVal); } var value = GetValues(); string s = value.S; 

... or completely destroy the tuple:

 public (string S, int I) GetValues() { // ... return (stringVal, intVal); } var (S, I) = GetValues(); string s = S; 

Tuples were not often used in C # pre-7 because they were cumbersome and verbose and were used only in cases where constructing a data class / structure for only one instance of the work would be more difficult than it was worth. But in C # 7, tuples now support the language level, so using them is much cleaner and more useful.

+18
Dec 11 '16 at 9:22
source share

I looked at the source of both Tuple and ValueTuple . The difference is that Tuple is a class , and ValueTuple is a struct that implements IEquatable .

This means that Tuple == Tuple will return false if they are not the same instance, but ValueTuple == ValueTuple will return true if they are of the same type, and Equals return true for each of the values ​​that they contain.

+8
Dec 11 '16 at 9:12
source share

Other answers forgot to mention important points. Instead of paraphrasing, I will refer to the XML documentation from the source code :

ValueTuple types (0 through 8) include a runtime implementation that underlies tuples in C # and structural tuples in F #.

In addition to the language created using the syntax , the easiest way to create them is with the help of the factory methods ValueTuple.Create . The types of System.ValueTuple differ from the types of System.Tuple in that:

  • they are more structures than classes,
  • they are volatile, not just readable , and
  • their members (such as Item1, Item2, etc.) are fields, not properties.

With the introduction of this type and C # 7.0 compiler you can easily write

 (int, string) idAndName = (1, "John"); 

And return two values ​​from the method:

 private (int, string) GetIdAndName() { //..... return (id, name); } 

Unlike System.Tuple you can update its members (mutable) because they are open fields for reading and writing, which can be given meaningful names:

 (int id, string name) idAndName = (1, "John"); idAndName.name = "New Name"; 
+6
Dec 11 '16 at 9:36
source share

In addition to the comments above, one unsuccessful getcha ValueTuple is that named arguments are erased when compiled to IL as a value type, so they are not available for serialization at runtime.

i.e. Your sweet named arguments will still display as "Item1", "Item2", etc. When serialized through, for example, Json.NET.

+6
Dec 22 '17 at 12:51 on
source share

Late accession to add a brief explanation to these two factoids:

  • they are structures rather than classes
  • they are volatile, not just read

One would think that mass changing tuples of values ​​would be simple:

  foreach (var x in listOfValueTuples) { x.Foo = 103; } // wont even compile because x is a value (struct) not a variable var d = listOfValueTuples[0].Foo; 

Someone might try to get around this this way:

  // initially *.Foo = 10 for all items listOfValueTuples.Select(x => x.Foo = 103); var d = listOfValueTuples[0].Foo; // 'd' should be 103 right? wrong! it is '10' 

The reason for this bizarre behavior is that value tuples are precisely based on values ​​(structures), and so the .Select (...) call works with cloned structures, not the originals. To solve this problem, we must resort to:

  // initially *.Foo = 10 for all items listOfValueTuples = listOfValueTuples .Select(x => { x.Foo = 103; return x; }) .ToList(); var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed 

As an alternative, of course, you can try a simple approach:

  for (var i = 0; i < listOfValueTuples.Length; i++) { listOfValueTuples[i].Foo = 103; //this works just fine // another alternative approach: // // var x = listOfValueTuples[i]; // x.Foo = 103; // listOfValueTuples[i] = x; //<-- vital for this alternative approach to work if you omit this changes wont be saved to the original list } var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed 

Hope this helps someone struggle to make tails from the values ​​listed in the list of tuples.

+2
Jul 05 '19 at 11:48
source share

This is a pretty good summary of what it is and its limitation.

https://josephwoodward.co.uk/2017/04/csharp-7-valuetuple-types-and-their-limitations

0
Jan 02 '18 at 2:32
source share



All Articles