C# 7 Series, Part 1: Value Tuples

Starting today I will start a new C# 7 series, to introduce new language features of C# 7+ features. Please note that I am not saying C# 7.0, I am saying C# 7 plus, because there will be minor language versions (like 7.1, 7.2) that will bring new features in steps (thanks to Roslyn!) such as async Main and default literals.

Tuples

The System.Tuple class provides a type to represent a key-value like property bag. It can be used where you want to have a data structure to hold an object with properties (elements) but you don’t want to create a separate type for it. The following code shows how you use it as a return value of a method that holds the name and the age of a student.

 public Tuple<string, int> GetStudentInfo(string id)
{     
    // Search by ID and find the student.
    return new Tuple<string, int>("Annie", 25);
}

As you can see, I am returning an instance of Tuple<string, int> object with first argument is the name and the second argument is the age. Later on we can have code to call this method, like this:

 public void Test()
{     
    Tuple<string, int> info = GetStudentInfo("100-000-1000");
    Console.WriteLine($"Name: {info.Item1}, Age: {info.Item2}");
}

You can access name and age by referencing Item1 and Item2.

The Tuple class has some obvious problems:

  • You need to access the properties with a name pattern ItemX, the property name may not make sense to the caller, that would be better if we can write something like info.Name and info.Age instead of info.;Item1 and info.Item2.
  • You are limited to have maximum 8 properties. If you want more, the last type argument must be another Tuple. This makes the syntax super hard to understand.
  • Tuple is a reference type, not like other primitive types (they are most value types), it allocates on heap and it could be too much object creation/allocation for CPU intensive operations.

Value Tuples

C# 7.0 introduced ValueTuple structure, which is a value type representation of the tuple object. The language team made many good things for this value tuple type, including a new syntax and many features (such as deconstruction.)

The following is a rewrite version with the value tuples, Note that if you don’t see the ValueTuple available in your project, you have to download the System.ValueTuple 4.3.0 NuGet package to your project. You don’t need to do anything if you are using .NET Framework 4.7 or higher, or .NET Standard Library 2.0 or higher.

 public (string, int) GetStudentInfo(string id)
{
    // Search by ID and find the student. 
    return ("Annie", 25);
}
 
public void Test()
{ 
    (string, int) info = GetStudentInfo("100-000-1000"); 
    Console.WriteLine($"Name: {info.Item1}, Age: {info.Item2}");
}

The code above is much simplified by using the first-class syntax ().

You can even give a name to every element in the value tuple, like this:

 public (string name, int age) GetStudentInfo(string id)
{
    // Search by ID and find the student.
    return (name: "Annie", age: 25);
}
 
public void Test()
{
    (string name, int age) info = GetStudentInfo("100-000-1000"); 
    Console.WriteLine($"Name: {info.name}, Age: {info.age}");
}

Perfect! Now you have good metadata for the elements in tuple objects, you then don’t need to go back and forth to make sure you are returning/accessing elements in the right order as its original definition.

The Visual Studio IDE will give you hints when you work with the value tuples.

image

image

Value Tuple Deconstruction

You can deconstruct the elements from the value tuple object, and access the local variables.

 // Deconstruct using the var (x, y) syntax,
// or (var x, var y) syntax.
var (name, age) = GetStudentInfo("100-000-1000");
// Now you have two local variables: name and age.
Console.WriteLine($"Name: {name}, Age: {age}");

If you just care about certain elements but not all, you can use the _ keyword to ignore the local variable.

 // Deconstruct using the var (x, y) syntax,
// or (var x, var y) syntax.
var (name, _) = GetStudentInfo("100-000-1000");
// Now you have just one local variable: name. The value for age is ignored.
Console.WriteLine($"Name: {name}");

Value Tuples to Tuples

The System.Tuple and System.ValueTuple provides some extension methods to help convert between Tuple and ValueTuple.

 var valueTuple = (id: 1, name: "Annie", age: 25, dob: DateTime.Parse("1/1/1993"));
var tuple = valueTuple.ToTuple();

Conclusion

ValueTuple makes the C# language more modern, and easy to use with simplified syntax. It solves many Tuple problems:

  • Value Tuple objects have first class syntax support, it simplifies the code to work with tuple elements.
  • You can associate a name with the value tuple element, you get some level of design time and compiler time validation of your code.
    • Please NOTE that the name associated with the tuple element is not a runtime metadata, i.e. there is no such a property/field with the name on the actual instance of that value tuple object, the property names are still Item1, Item2, etc., all element names are design time and compiler time only.
  • You are now flexible to access all tuple elements, or some of them, by using the deconstruction and the _ keyword.
  • Value Tuple types are value types, no inheritance or other features, this means that the value tuples are better performance.

Since the name of the value tuple element is not runtime, you have to be careful using it when doing serialization with existing libraries, such as Newtonsoft.Json, Unless the library updated to support the new metadata (TupleElementNameAttribute etc.) before then you would run into bugs.