With C#6 nearing completion, plans are already being laid for C# 7. While nothing is definite yet, they are starting to categorize proposals in terms of “interest and estimated plausibility”. In this series, we’ll be looking at some of the proposals starting with language support for tuples.
The purpose of a tuple is to create a lightweight way to return multiple values from a function. Good tuple support eliminates the need for out parameters, which are usually considered to be cumbersome. Moreover, out parameters are incompatible with async/await, making them useless in many scenarios.
What about the Tuple class?
The .NET Framework has a Tuple class since version 4. However, most developers consider it to only be useful under very limited circumstances. First of all, Tuple is a class. This means that memory has to be allocated when using it, which in turn increases memory pressure and makes GC cycles more frequent. For it to compete with out parameters in terms of performance it needs to be a structure.
The second issue involves API design. If you see a return type of Tuple<int, int> that doesn’t really tell you anything. Every use of the function would require checking the documentation twice, once when writing it and again during the code review. It would be far more useful if the return type were something like Tuple<int count, int sum>.
Anonymous Structs
Consider these lines:
public (int sum, int count) Tally(IEnumerable<int> values) { ... }
var t = new (int sum, int count) { sum = 0, count = 0 };
Under the proposal, either line would define a new anonymous value type with sum and count properties. Note that unlike an anonymous class, the anonymous struct requires you to explicitly list the property names and types.
A benefit of using structs is that they define Equals and GetHashCode automatically. Though one could argue that the default implementation isn’t very efficient and the compiler should provide one instead.
Unpacking Tuples
An important part of the tuple proposal is the ability to unpack tuples with a single line of code. Consider this block of code:
var t = Tally(myValues);
var sum = t.Sum;
var count = t.Count;
With unpacking, it becomes simply:
(var sum, var count) = Tally(myValues);
Not yet decided is whether or not you can unpack a tuple without declaring new variables. Or in other words, can you omit ‘var’ and use a pre-existing local variable instead.
Returning Tuples
There are two proposals being considered for how tuples would be returned from a function. The first is fairly easy to understand:
return (a, b);
The second option has no return statement at all. Consider this example,
public (int sum, int count) Tally(IEnumerable<int> values)
{
sum = 0; count = 0;
foreach (var value in values) { sum += value; count++; }
}
Implicitly created local/return variables isn’t a new concept. Visual Basic was originally designed that way, though it became unpopular once VB 7 introduced the return statement. It also mirrors what you would write if you were using out parameters. Still, not seeing a return statement would be somewhat disconcerting to many developers.
Other Issues
Tuple support is a rather complex topic. While this article covers the day-to-day aspects, many details will have to be resolved from the compiler writer /advanced user perspective.
Should tuples be mutable? This could be useful from a performance or convenience standpoint, but may make the code more error prone, especially when dealing with multi-threading.
Should tuples be unified across assemblies? Anonymous types are not unified, but unlike anonymous types these will be exposed as part of an API.
Can tuples be converted into other tuples? Superficially, if they have the same type structure but different property names. Or the same property names, but wider property types.
If you pass a tuple of two values to a function that takes two parameters, will be tuple be automatically unpacked (splatted)? Conversely, can you “unsplat” a pair of arguments into one tuple parameter?