<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. C#
  3. Functional C# and LINQ
All lessons in this course

Lesson 44 of 60 11 minIntermediate

Tuples and deconstruction

Group a few values without writing a class, return several results from a method, and unpack them into variables.

Sometimes a method naturally produces several results: minimum and maximum, a success flag and a value, a coordinate pair. Writing a class for each is overkill, and out parameters are clumsy. A tuple groups a few values into one lightweight value.

Tuples.cs
(string, double) a = ("B1", 6.0);             // unnamed: Item1, Item2(string Mark, double Length) b = ("B2", 4.5); // named elementsvar c = (Mark: "B3", Length: 7.2);            // names in the literal Console.WriteLine(a.Item1);Console.WriteLine(b.Mark);Console.WriteLine(c);                          // nice ToString
Output
B1
B2
(B3, 7.2)

Returning several values

Interactive

Return several values at once

static (double Min, double Max, double Avg) Stats(double[] v)
=> (v.Min(), v.Max(), v.Average());
(
Min8.5
,
Max40
,
Avg20.75
)
Discard with _:
var (min, _, avg) = Stats(loads);
// min = 8.5
// avg = 20.75
Deconstruction unpacks the tuple into separate variables by position. _ throws away values you don’t need.
Stats.cs
static (double Min, double Max, double Avg) Stats(double[] v)    => (v.Min(), v.Max(), v.Average()); var s = Stats(new[] { 12.5, 40, 22, 8.5 });Console.WriteLine($"min {s.Min}, max {s.Max}, avg {s.Avg}");
Output
min 8.5, max 40, avg 20.75

Deconstruction

Deconstruct.cs
var (min, max, avg) = Stats(new[] { 12.5, 40, 22, 8.5 });   // three new variablesvar (_, top, _) = Stats(new[] { 3.0, 9.0 });                 // _ discardsConsole.WriteLine($"{min} {max} {avg} {top}"); // swap without a temporary variableint x = 1, y = 2;(x, y) = (y, x);Console.WriteLine($"{x} {y}"); // deconstruct in a foreach over a dictionaryvar weights = new Dictionary<string, double> { ["IPE200"] = 22.4, ["IPE300"] = 42.2 };foreach (var (profile, kg) in weights)    Console.WriteLine($"{profile}: {kg}");
Output
8.5 40 20.75 9
2 1
IPE200: 22.4
IPE300: 42.2

Deconstructing your own types

Point.cs
public readonly struct Point3(double x, double y, double z){    public double X { get; } = x;    public double Y { get; } = y;    public double Z { get; } = z;     public void Deconstruct(out double x, out double y, out double z)        => (x, y, z) = (X, Y, Z);} var (px, py, _) = new Point3(1, 2, 3);Console.WriteLine($"{px}, {py}");// records get Deconstruct automatically from their positional parameters
Output
1, 2

Tuples vs classes and records

UseWhen
tuplea quick, local grouping: a private method’s result, a LINQ intermediate value
recordthe data has meaning across your program (a Section, an ExportResult) or is public API
classthe data also has behaviour and identity
Quick check

What does var (a, _) = (5, 9); do?

Quick check

How do you swap x and y with tuples?

Practice

Write static (int Quotient, int Remainder) Divide(int a, int b) and use deconstruction to print 17 ÷ 5 as “3 remainder 2”. Then use it to convert 200 minutes into hours and minutes.

Show the solution
Solution.cs
static (int Quotient, int Remainder) Divide(int a, int b) => (a / b, a % b); var (q, r) = Divide(17, 5);Console.WriteLine($"{q} remainder {r}"); var (hours, minutes) = Divide(200, 60);Console.WriteLine($"{hours} h {minutes} min");
Output
3 remainder 2
3 h 20 min

Remember

  • Tuples group a few values: (string Mark, double Length).
  • Return tuples to give back several results without out or a new class.
  • Deconstruct with var (a, b) = …; use _ to discard.
  • Prefer records/classes for data that matters beyond one method.
0 of 60 lessons completed