Tuples in C# 7

A tuple is an finite ordered list of values, of possibly different types, which is used to bundle related values together without having to create a specific type to hold them.

In .NET 4.0, a set of Tuple classes has been introduced in the framework, which can be used as follows:

private static Tuple<int, double> Tally(IEnumerable<double> values)
{
	int count = 0;
	double sum = 0.0;
	foreach (var value in values)
	{
	    count++;
	    sum += value;
	}
	return Tuple.Create(count, sum);
}

...

var values = ...
var t = Tally(values);
Console.WriteLine($"There are {t.Item1} values and their sum is {t.Item2}");

There are two annoying issues with the Tuple classes:

  • They’re classes, i.e. reference types. This means they must be allocated on the heap, and garbage collected when they’re no longer used. For applications where performance is critical, it can be an issue. Also, the fact that they can be null is often not desirable.
  • The elements in the tuple don’t have names, or rather, they always have the same names (Item1, Item2, etc), which are not meaningful at all. The Tuple<T1, T2> type conveys no information about what the tuple actually represents, which makes it a poor choice in public APIs.

In C# 7, a new feature will be introduced to improve support for tuples: you will be able to declare tuples types “inline”, a little like anonymous types, except that they’re not limited to the current method. Using this new feature, the code above becomes much cleaner:

static (int count, double sum) Tally(IEnumerable<double> values)
{
	int count = 0;
	double sum = 0.0;
	foreach (var value in values)
	{
	    count++;
	    sum += value;
	}
	return (count, sum);
}

...

var values = ...
var t = Tally(values);
Console.WriteLine($"There are {t.count} values and their sum is {t.sum}");

Note how the return type of the Tally method is declared, and how the result is used. This is much better! The tuple elements now have significant names, and the syntax is nicer too. The feature relies on a new ValueTuple<T1, T2> structure, which means it doesn’t involve a heap allocation.

You can try this feature right now in Visual Studio 15 Preview 3. However, the ValueTuple<T1, T2> type is not (yet) part of the .NET Framework; to get this example to work, you’ll need to reference the System.ValueTuple NuGet package.

Finally, one last remark about the names of tuple members: like many other language features, they’re just syntactic sugar. In the compiled code, the tuple members are only referred to as Item1 and Item2, not count and sum. The Tally method above actually returns a ValueTuple<int, double>, not a specially generated type. Note that the compiler that ships with VS 15 Preview 3 emits no metadata about the names of the tuple members. This part of the feature is not yet implemented, but should be included in the final version. This means that in the meantime, you can’t use tuples across assemblies (well, you can, but you will lose the member names and will have to use Item1 and Item2 to refer to the tuple members).

11 thoughts on “Tuples in C# 7”

  1. Will the reverse of inline declaration work too:

    (var count, var sum) = Tally(values)

    That’s what’s also missing in my opinion.

  2. Hi Markus,

    Good question. As far as I can tell, tuple deconstruction isn’t implemented yet, and doesn’t appear in the specs for this feature. However, it is mentioned several times in the original issue where the feature is discussed… I hope the idea haven’t been abandoned.

    1. Hi Harry,

      Yes, tuples can be nested:

      public static (int a, (int x, int y) b) Test()
      {
          return (1, (2, 3));
      }
  3. Any thoughts on why they didn’t have the compiler just generate a type with real named fields to avoid problems with reflection, serialization, and all the other issues that comes with the fields not actually being named?

    1. Hi Mike,

      If they had done that, tuples would suffer the same problem as anonymous types; they couldn’t be shared across assemblies, i.e. a type (int x, int y) defined in assembly A wouldn’t be the same as a type (int x, int y) defined in assembly B.

      1. Sure, but why would that matter – these are value types not objects, so mapping from one to the other is almost free…there’s no garbage being created. Every assembly can create its own types, and when you call a method cross-assembly that uses or returns a value tuple, it just maps the values. I imagine it’s the exact same runtime cost as copying the value.

Leave a Reply

Your email address will not be published. Required fields are marked *