Using Tuples and deconstruction to return multiple values in C#

Full article

One of the biggie challenges when programming in any language is figuring how to group and organize things sensibly. Even a small project can get out of hand quickly, and once you've got a dozen devs working in something for years, all bets are off.

So we have methods and functions, organized into classes (even JS has classes now) and namespaces, separate projects and assemblies, and on and on. The tricky part of having so many ways to organize things is knowing how and when to use one of them. A lot of it's up for debate (what isn't?) and some of it's not... I wouldn't recommend publish a NuGet package for a few lines of code, for example. ๐Ÿ™„

In C#, it's not uncommon to use classes to group similar logic together, and then provide properties to access whatever values are in the class - a person with a name and birthdate, a car with an engine type and model, whatever. If you're going to have lots of properties, a class probably makes sense. But what if you don't? If there's a more lightweight option than a class and a good use case for it, I'd be interested in hearing about it.. or writing about it as the case may be.

public class Circle
{
    int radius;

    public Circle(int radius) => this.radius = radius;

    public int Diameter => radius * 2;
    public double Circumference => Math.PI * Diameter;
    public double Area => Math.PI * Math.Pow(radius, 2);
}

One alternative is to create a method with several "out" parameters.

private void GetCircle(int radius, out int diameter, out double circumference, out double area)
{
    diameter = radius * 2;
    circumference = Math.PI * diameter;
    area = Math.PI * Math.Pow(radius, 2);
}

I used to think that was ugly since we had to define the variables before calling the method, but now they can be defined right as we call the method so it's a lot more palatable. That was introduced in C# 7.0.

GetCircle(6, out var diameter, out var circumference, out var area);

Console.WriteLine($"Diameter: {diameter}");               // 12
Console.WriteLine($"Circumference: {circumference:N2}");  // 37.70
Console.WriteLine($"Area: {area:N2}");                    // 113.10

Another alternative, and the one I want to really focus on, is using tuples. I used to like them even less than "out" parameters, since whatever values you returned were only accessible by referencing .Item1, .Item2, etc. You immediately lost context of what was being returned.. very unfriendly. There have been some major changes though since C# 7.0, and it makes tuples much easier on the eyes.. brain... something. They're easier to work with.

Here's an example that's pretty similar to the one above using "out" parameters, but it returns a tuple type instead. Since tuple types can have field names too, this really seems to behave more like a class to me.

private (int diameter, double circumference, double area) GetCircle(int radius)
{
    var diameter = radius * 2;
    var circumference = Math.PI * diameter;
    var area = Math.PI * Math.Pow(radius, 2);

    return (diameter, circumference, area);
}

Being able to access each element of the tuple by name makes this soo much friendlier than in the past.

// Access each tuple element individually
var circle = GetCircle(3);

Console.WriteLine($"Diameter: {circle.diameter}");               // 6
Console.WriteLine($"Circumference: {circle.circumference:N2}");  // 18.85
Console.WriteLine($"Area: {circle.area:N2}");                    // 28.27

// Access the tuple element directly
Console.WriteLine($"Diameter: {GetCircle(10).diameter}");        // 20

You can desconstruct a tuple if you'd like, assigning all the elements in one place, which is similar to how the "out" example works.

// Deconstruct the tuple elements into separate local variables
var (diameter, circumference, area) = GetCircle(4);

Console.WriteLine($"Diameter: {diameter}");                      // 8
Console.WriteLine($"Circumference: {circumference:N2}");         // 25.13
Console.WriteLine($"Area: {area:N2}");                           // 50.27

And if you don't need all of the elements, you can even discard them. Here's an example where all I needed was the area of the circle, so I discarded the rest.

// Deconstruct the tuple elements and discard the ones you don't need
var (_, _, area) = GetCircle(5);

Console.WriteLine($"Area: {area:N2}");                          // 78.54

As usual, all the above code snippets are on GitHub, so grab the code if you want to play around with it more on your own.

What do you think? Like it more than classes and "out" variables? Love it? Hate it? Or do you have your own way of returning multiple values at once?

Author

Grant Winney

I write when I've got something to share - a personal project, a solution to a difficult problem, or just an idea. We learn by doing and sharing. We've all got something to contribute.


Comments / Reactions

One of the most enjoyable things about blogging is engaging with and learning from others. Leave a comment below with your questions, comments, or ideas. Let's start a conversation!