C# 7.0: Tuple Literals and Types
In this article, we check out what new features C# 7.0 brings to tuples in C#! And we've even got some sample code included!
Join the DZone community and get the full member experience.
Join For FreeC# 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 a practical example that demonstrates how to move on classic method that returns multiple values to new tuples.
I'll take an example from practice and modify it a little bit. Suppose we have a method in a business layer that, based on given coordinates, finds the nearest store. We need to return two values: distance and at least something about the store. Before tuples, we had to use out-parameter or some special model class for the 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 a situation when the store is null and in this case, it returns –1 as the 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 the tuple class, but this solution is not perfect. Take a look at how we used the returned value in the 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 by providing tuple types and literals. In C# 7.0 we can write the GetNearestStore()
method using a 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. The example given here is a good example of when to go with tuple literals.
Note: To use tuple literals add a NuGet package reference to the System.ValueTuple package.
We also win on the calling code side, as we get rid of those additional variables that made the code less 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 similarly to regular tuples. It is actually possible to use the ToTuple()
extension method of ValueTuple to cast value tuples to regular tuples if needed. This may be a good option if we have a new code that uses value tuples and there is an older interface that expects classic tuples.
Note: With tuple literals, we still get a generic type per returned set of parameters. Now, one may ask, why don't 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 the load of the model classes as we have a mechanism for these available in the framework.
Wrapping Up
We started with the classic example of a method that has to return a domain object and some parameter not directly related to it. We went through different options of 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. The last two code samples demonstrated how we achieved better code readability with tuple literals.
Published at DZone with permission of Gunnar Peipman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments