C#7 - New ValueTuple Will Help You Write Less Code
05 June 2017

Why do we care for Tuple at all?

Do you like functions like TryParse? Saying that it is ugly is an understatement. Function is returning two values bool success and int number but we are getting one as a regular returned value and second one as out parameter. This is not only inconsistent approach, it will also not work for async functions:

[Fact]
public void PainBeforeTuples()
{
var numTxt = "555";
int num;
bool res = int.TryParse(numTxt, out num);
if (res)
{
Assert.Equal(555, num);
}
else
{
Assert.True(false, "Make sure if-true executed");
}
}

In situations like this people are using all kind of tricks like returning Pair or KeyValuePair just to cut overhead of defining a class or structure. Designing a class make sense only if you are expressing design intent and need to add some behaviour rather than just move data around.

Why System.Tuple in .NET 4 was not that great

Some languages are allowing returning multiple values (complex result) and following that Microsoft provided something similar by introducing System.Tuple in .NET 4. Problem with this solution is that member names are not customizable. They are always Item1, Item2 and so on. Also, System.Tuple is a reference type, therefore it is heap allocated and garbage collected. That is not that bad if you are doing more copying (assignment) than creation. Assuming that main usage is complex function result, reference type is unnecessarily stressing GC.

New C#7 feature and System.ValueTuple library to the rescue

With new C#7 feature and library defining generic structure System.ValueTuple we got much more elegant solution for returning multiple values. In order to use it you must include NuGet package in your project. (Update Aug 15, 2017: Starting from .Net Core 2.0 you don't need to explicitly include ValueTuple package)
What is ValueTuple? Think of it like an unnamed type. Good news, you can give semantic name to a field instead using predefined Item1, Item2, ..., but you cannot add any behaviour to it.

First let's clarify some terminology:

  • tuple type - (string, int)
  • tuple literal - ("Big Hero", 6)
  • tuple literal with named elements - (name: "Big Hero, version: 6)

Creation

Let's create some tuples. Unnamed System.ValueTuple is similar to the old System.Tuple except it is a value type and will go on a stack. Since no names are provided, fields are named as Item*:

// "Unnamed tuples" - Similar to old System.Tuple
var hero_1 = ("Big Hero", 6);
Assert.Equal("Big Hero", hero_1.Item1);
Assert.Equal(6, hero_1.Item2);

Using named tuple you can give semantic names to the fields (although generic Item* naming is still available):

// "Named Tuple" - Using semantic names for tuple members
(string name, int version) hero_2 = ("Big Hero", 6);
Assert.Equal("Big Hero", hero_2.name);
Assert.Equal(6, hero_2.version);

// Named tuple still have Item1, Item2 ... fields.
Assert.Equal("Big Hero", hero_2.Item1);
Assert.Equal(6, hero_2.Item2);

You can provide names in the literal on the right side:

// You can specify names on the right side
var hero_3 = (name: "Big Hero", version: 6);
Assert.Equal("Big Hero", hero_3.name);
Assert.Equal(6, hero_3.version);

If you specify names on both sides, right ones will be ignored and you will get compiler warning:

// Specifying names on both sides will generate warning: right side names are ignored !
(string name, int version) hero_4 = (who: "Big Hero", number: 6);
Assert.Equal("Big Hero", hero_4.name);
Assert.Equal(6, hero_4.version);

Changing

Fields are public and mutable. Since System.ValueTuple doesn't have any "logic" that can put some constraints on data, making fields immutable would be useless.

// Fields are public and mutable
var hero_3 = (name: "Big Hero", version: 6);
hero_3.version++;
Assert.Equal(7, hero_3.version);

Assignment

Assignment is possible if number of fields (called arity) and types are the same. If types are not the same implicit conversion will be done. Field names are not assigned! Values are assigned according to the order. For example:
(string, int) and ValueTuple<string, int> are "same type".
(string name, int version) is not the "same type" but "convertible" to and from previous two.
Also (string, int) could be converted to (string, double) from example below:

[Fact]
public void AssignmentTest()
{
var hero_unnamed = ("Big Hero", 6);
var hero_named = (name: "Big Hero - NEW", version: 7);

// As long as number of fields is same and field types can be converted you can assign
hero_unnamed = hero_named;
Assert.Equal("Big Hero - NEW", hero_unnamed.Item1); // Names are not assigned !
Assert.Equal(7, hero_unnamed.Item2);

// Implicit conversion
(string, double) hero_converted = hero_named;
Assert.Equal(7, hero_converted.Item2);
Assert.True(hero_converted.Item2.GetType() == typeof(double));
}

Deconstruction

Finally, example how we can better write functions like TryParse. Divide function from example below is returning complex result and we can get result into one variable of type System.ValueTuple or deconstruct it to two variables:

// Next method is returning named tuple but we can just return (bool, int) as unnamed tuple
(bool success, int value) Divide(int a, int b)
{
var bIsNotZero = b != 0;
return (success: bIsNotZero, value: (bIsNotZero) ? a/b : 0);
}

[Fact]
public void ValueTupleAsFunctionResultTest()
{
// Using real names instead of Item1, Item2
var result = Divide(5, 2);
Assert.True(result.success);
Assert.Equal(2, result.value);

// Deconstruction into two variables
(var s, var v) = Divide(12, 3);
Assert.True(s);
Assert.Equal(4, v);
}

But there is more! You can provide deconstruction for any type in .NET by implementing Deconstruct method as a member of a class:

public class Hero
{
public Hero(string name, int version)
{
Name = name;
Version = version;
}

public string Name { get; }
public int Version { get; }

public void Deconstruct(out string name, out int version)
{
name = Name;
version = Version;
}

public (int version, int nextVersion) GetVersions()
{
return (Version, Version + 1);
}
}

Even if you can't change class you can make Deconstruct as an extension method:

public static class ValueTupleTestExtensions
{
// If you already have Deconstruct (with same signature) defined in Hero class this extension method will not be used!
public static void Deconstruct(this ValueTupleTest.Hero h, out string name, out int version)
{
name = h.Name + "-EXT";
version = h.Version;
}
}

If you have both defined (class and extension Deconstruct) and both are with the same signature, class method will be used.

While using deconstruction, you can explicitly declare field types or implicitly declare them by using var on the left side. You can even mix and have one var only. You can deconstruct to existing variables. Also, you can ignore some values by using "discards" symbol "_":

[Fact]
public void DeconstructionTest()
{
var hero = new Hero("Big Hero", 6);

// Explicitly declare field types
(string name, int version) = hero;

Assert.Equal("Big Hero", name);
Assert.Equal(6, version);

// Implicitly declare field types by using var
var (name2, version2) = hero;
Assert.True(name2.GetType() == typeof(string));
Assert.True(version2.GetType() == typeof(int));

// You can even mix explicit and implicit types
(string name3, var version3) = hero;
Assert.True(version3.GetType() == typeof(int));

// Deconstruct to existing variables
(name2, version3) = hero;

// Using "discards" - "I don't care for the rest"
(var name4, _ ) = hero;
Assert.Equal("Big Hero", name4);
}

You will like LINQ even more

With LINQ I'm abusing anonymous types even though they are not ideal solution. Apart from being reference type, there is a huge limitation that anonymous type can not be returned from the function. That is not the case with System.ValueTuple. You can return IEnumerable<> of it and finally enjoy layering your LINQ queries in multiple functions:

public IEnumerable<(string name, int version)> GetHeroes()
{
var heroes = new List<Hero>()
{
new Hero("Big Hero", 6),
new Hero("Small Hero", 5)
};

// LINQ
return heroes.Select(x => (x.Name, x.Version));
}

[Fact]
public void LinqTest()
{
var heroes = GetHeroes().ToList();

// Names are preserved !
Assert.Equal("Big Hero", heroes[0].name);
Assert.Equal(6, heroes[0].version);
Assert.Equal("Small Hero", heroes[1].name);
Assert.Equal(5, heroes[1].version);
}

Nullable it is

ValueTuple is a value type so go ahead and make it nullable:

[Fact]
public void NullableTest()
{
(int, int)? numbers = null;
Assert.False(numbers.HasValue);

numbers = (1, 2);
Assert.True(numbers.HasValue);
}

Still not baked to perfection

If you look at decompiled source you will find that names are just listed as synonyms in TupleElementNames attribute. Those are handled by compiler and Roslyn API within the same assembly and will be replaced with Item* once you cross that boundary. Names will be also lost when casting to dynamic or serializing to JSON. Since names are stripped, having class with two properties with tuples different only by names will generate compilation error.

Using System.ValueTuple in ASP.NET

I was hoping that I can get away with creating System.ValueTuple model in MVC but names are lost. Still you can decompose to something more readable:

MVC Controller

public class ExampleCSharpController : Controller
{
public IActionResult ValueTuple()
{
return View(("Too lazy to create a model", 555));
}
}

MVC View

@model ValueTuple<string, int>

@{
ViewData["Title"] = "C# ValueTuple";
(string text, int number) = Model;
}

<b>Text: @text</b></br>
<b>Number: @number</b>

Not a perfect solution since you rely on order! I'm sure names will cross this border in future language iterations.

(Update Aug 15, 2017: Starting from ASP.Net Core 2.0 you don't need extra step to include C#7 options for Razor)
In order to get C#7 features in MVC Core View you must include package:

<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.2.0" />

While configuring services and adding MVC you must include C#7 options for Razor:

services.AddMvc().AddRazorOptions(options =>
options.ParseOptions = new CSharpParseOptions(LanguageVersion.CSharp7));

How it's made

We already know that names are just friendly synonyms stored in attribute. But how come we can define tuple with any number of elements? If you look at definition you will find that maximum number of fields is 7, but there is another field called Rest. When you call Item8 in the background is emitted Rest.Item1! After Rest.Item7 you will get Rest.Rest.Item1:

[Fact]
public void RestTest()
{
var numbers = (1, 2, 3, 4, 5, 6, 7, 8, 9);
Assert.Equal(7, numbers.Item7);
// Rest
Assert.Equal(8, numbers.Item8); // You still can call Item8 but will be converted to Rest.Item1
Assert.Equal(8, numbers.Rest.Item1);
Assert.Equal(9, numbers.Item9);
Assert.Equal(9, numbers.Rest.Item2);
}

That is probably all you need to know about System.ValueTuple for now. I like it mainly because it helps me write less code.