static void

C#7 ValueTuples

Published Monday 20 February 2017

More new things in C#7 / Visual Studio 2017 (see yesterday on pattern matching). Remember C#7 isn't the same a .Net version upgrade - you can use these language features in projects targeting .net Core, .net 4.6, 4.5 and even 4.0 (and sometimes in v2/v3.5).

Normally a method can return only one value. It can return an int, or a string, or a custom class.

public int Process(Product p)
public string Process(Order o)
public List<Product> ProcessOrders(Order o)

Creating a new class may be overkill just to return a few values. The classic example is TryParse, when you want a boolean result, and the parsed value if available. The framework solves this with the awkward "out" parameter.

int i;
if (int.TryParse(s, out i))
{
    AddToTotal(i);
}

Since .net 4.0, we had Tuples as a "bucket" for returning simple values, but the "Item1"/"Item2" properties aren't very helpful.

public Tuple<stringint> ParseCat(Cat cat)
Tuple<stringint> tuple = ParseCat(cat);
var name = tuple.Item1;
var age = tuple.Item2;

If you're really desperate you can use dynamic types, with a performance hit.

C#7 provides two different improvements which help.

"out" paramters

In C#7 you no longer have to stick the variable declaration above the method call.

//int i; no longer needed!!
if (int.TryParse(s, out int i))
{
    AddToTotal(i);
}

It's a small change, but probably the most useful one in C#7. This one works in .net 2.0 projects too.

TupleValue

There's a new package called System.ValueTuple which is required for this. It works for .net 4.0, 4.5 and later. Ignoring the language features, this is what it looks like if you were coding in VS2015.

public System.ValueTuple<boolint> TryParse(string s)
{
    var b = int.TryParse(s, out int i);
    return new ValueTuple<boolint>(b, i);
}

So this is a TryParse without the "out" parameter, and it looks a lot like the old Tuple.

var result = TryParse("123");
if (result.Item1)
{
    AddToTotal(result.Item2);
}

The magic happens because C#7 internalizes the System.ValueTuple, so what you write in VS2017 looks like this:

public (boolint) TryParse(string s)
{
    var b = int.TryParse(s, out int i);
    return (b, i);
}

"ValueTuple" has disappeared, and we just see the types, within brackets. Now, you can name the return values.

public (bool IsOk, int Value) TryParse(string s)
{
    var b = int.TryParse(s, out int i);
    return (b, i);
}

Calling code can still use the obscure "Item1"/"Item2" names, but in VS2017 you can use the defined names.

var result = TryParse("123");
if (result.IsOk)
{
    AddToTotal(result.Value);
}

The calling code can also use the bracket syntax so the hidden tuple disappears completely.

(bool isOk, int value) = TryParse("123");
if (isOk)
{
    AddToTotal(value);
}

Or just use "var" around the whole result.

var (isOk, value) = TryParse("123");
if (isOk)
{
    AddToTotal(value);
}

All the awkward details of the underlying tuple are invisible, and the code is clean and readable.

Use with caution

Obviously if you're returning more than one value and you really don't want to return a class, check your design. "TryParse" or "TryLoad" methods are a good example where the pattern can make sense.

Previously: C#7 Pattern Matching (19 February 2017)