Tuple literals in C# 7.0
C# 7.0 brings some new features to tuples and with code editors that support these new features we can use tuples with named members instead of names like Item1…ItemN. This blog post goes through practical example that demonstrates how to move on classic method that returns multiple values to new tuples.
I take example from practice and modify it a little bit. Suppose we have a method in business layer that based on given coordinates finds a nearest store. We need to return two values: distance and at least something about store. Before tuples we had to use out-parameter or some special model class for return value.
public double GetNearestStore(double lat, double lon, out Store store)
{
store = _storeLocator.FindNearest(lat, lon);
if(store == null)
{
return -1;
}
return _storeLocator.DistanceFrom(store, lat, lon);
}
Moving to tuples
The same code can be refactored to tuples. Suppose here that store locator can handle situation when store is null and in this case it returns –1 as distance.
public Tuple<double, Store> GetNearestStore(double lat, double lon)
{
var store = _storeLocator.FindNearest(lat, lon);
var distance = _storeLocator.DistanceFrom(store, lat, lon);
return new Tuple<double, Store>(distance, store);
}
Tuples let us use two return values through Tuple class but this solution is not perfect. Take a look how we use returned value in calling code.
public string GetNearestStoreMessage()
{
var nearestStore = GetNearestStore(_customer.Lat, _customer.Lon);
var messageTemplate = "Nearest store {0} is {1} km away";
return string.Format(messageTemplate,
nearestStore.Item2.Name,
nearestStore.Item1);
}
We get values back easily but with price of decreased readability. Item1 and Item2 tell us almost nothing. It can even get more confusing when we return more values with same tuple. Okay, we can use additional variables like here.
public string GetNearestStoreMessage()
{
var nearestStore = GetNearestStore(_customer.Lat, _customer.Lon);
var messageTemplate = "Nearest store {0} is {1} km away";
var storeName = nearestStore.Item2.Name;
var distance = nearestStore.Item1;
return string.Format(messageTemplate, storeName, distance);
}
It’s better but not yet perfect. We introduced two new variables just to keep code readable. And we end up with even more code when tuple contains more parameters.
Introducing tuple literals
C# 7.0 solves this problem for us providing tuple types and literals. In C# 7.0 we can write GetNearestStore() method using more readable syntax.
public (double distance, Store store) GetNearestStore(double lat, double lon)
{
var store = _storeLocator.FindNearest(lat, lon);
var distance = _storeLocator.DistanceFrom(store, lat, lon);
return (distance, store);
}
It works well if we don’t have many values to return and the example here is good example when to go with tuple literals.
NB! To use tuple literals add NuGet package reference to System.ValueTuple package.
We also win on calling code side as we get rid of those additional variables that made code more readable.
public string GetNearestStoreMessage()
{
var nearestStore = GetNearestStore(_customer.Lat, _customer.Lon);
var messageTemplate = "Nearest store {0} is {1} km away";
return string.Format(messageTemplate,
nearestStore.store.Name,
nearestStore.distance);
}
Our code is now more readable and it covers the called method and calling code. Cool, isn’t it?
Internally the support for tuple literals is based on ValueTuple classes that behave similar to regular tuples. It is actually possible to use ToTuple() extension method of ValueTuple to cast value tuples to regular tuples if needed. This may be good option if we have new code that uses value tuples and there are older interface that expect classic tuples.
NB! With tuple literals we still get generic type per returned set of parameters and now one may ask why we don’t use our own model classes to return values? We can do it, sure, no problem. But when using tuple literals we don’t have to define load of model classes as we have mechanism for these available by framework.
Wrapping up
We started with classic example of method that has to return domain object and some parameter not directly related to it. We went through different options how to do it and finally stopped on tuple literals provided by C# 7.0. With tuple literals we have tuple members with custom names and we don’t have to use these Item1…ItemN member names. Last two code samples demonstrated how we achieved better code readability with tuple literals.