C# tuple
last modified November 6, 2026
In this article we show how to work with a tuple type in C#.
A tuple is a grouping of ordered values. The values can be of
different types and may optionally be named. Tuples can either be reference
types (backed by System.Tuple) or value types (backed by
System.ValueTuple).
Since C# 7, tuples are part of the language syntax. The syntax uses round
brackets to define and work with tuples, which are backed by
System.ValueTuple. In this article we focus on this syntax.
Tuples are handy for grouping a few related values without defining a new
type; for data that is passed around widely, a record or a class is usually a
better choice.
C# tuple simple example
In the following example, we create a new tuple using the old and the new way.
var u1 = Tuple.Create("John", "Doe", "gardener");
Console.WriteLine($"{u1.Item1} {u1.Item2} is a {u1.Item3}");
var u2 = ("Roger", "Roe", "driver");
Console.WriteLine($"{u2.Item1} {u2.Item2} is a {u2.Item3}");
Console.WriteLine(u1.GetType());
Console.WriteLine(u2.GetType());
We create two tuples and print their contents to the console.
var u1 = Tuple.Create("John", "Doe", "gardener");
Console.WriteLine($"{u1.Item1} {u1.Item2} is a {u1.Item3}");
This is the old way of creating tuples. A new instance of
System.Tuple is created. The members of the tuple are accessed
with ItemN properties. This tuple is a reference type and its
members are read-only.
var u2 = ("Roger", "Roe", "driver");
Console.WriteLine($"{u2.Item1} {u2.Item2} is a {u2.Item3}");
Here is the modern way of creating a tuple. This tuple is a value type. We
access the tuple fields with Item1, Item2, and
Item3; however, it is possible to name the fields.
Console.WriteLine(u1.GetType()); Console.WriteLine(u2.GetType());
We print the underlying types of the two tuples.
$ dotnet run John Doe is a gardener Roger Roe is a driver System.Tuple`3[System.String,System.String,System.String] System.ValueTuple`3[System.String,System.String,System.String]
C# tuple field names
We can name our tuple fields.
var u1 = (Name: "John Doe", Age: 34);
Console.WriteLine($"{u1.Name} is {u1.Age} years old");
(string Name, int Age) u2 = ("Roger Roe", 55);
Console.WriteLine($"{u2.Name} is {u2.Age} years old");
var name = "Jane Doe";
var age = 26;
var u3 = (name, age);
Console.WriteLine($"{u3.name} is {u3.age} years old");
Console.WriteLine(u3.Item1);
Console.WriteLine(u1);
The example shows three different ways to name tuple fields.
var u1 = (Name: "John Doe", Age: 34);
The field name is separated from its value with a colon character. The fields are separated with commas.
Console.WriteLine($"{u1.Name} is {u1.Age} years old");
Here we refer to the tuple fields by their names.
(string Name, int Age) u2 = ("Roger Roe", 55);
We can specify the names and the types of the fields inside a pair of round brackets before the variable name.
var name = "Jane Doe"; var age = 26; var u3 = (name, age);
When a tuple is created from existing variables, the compiler infers both the types and the names of the fields from the variables. This feature is called inferred tuple element names.
Console.WriteLine(u3.Item1); Console.WriteLine(u1);
The names exist only at compile time; the ItemN fields are still
available. The ToString method of a tuple prints its values in
round brackets.
$ dotnet run John Doe is 34 years old Roger Roe is 55 years old Jane Doe is 26 years old Jane Doe (John Doe, 34)
C# value tuples are mutable
Unlike System.Tuple, the fields of a value tuple can be modified.
Since it is a value type, assignment copies the whole tuple.
var p = (X: 1, Y: 2); p.X = 10; Console.WriteLine(p); var q = p; q.Y = 20; Console.WriteLine(p); Console.WriteLine(q);
We change a field of a tuple and copy the tuple to another variable.
var q = p; q.Y = 20;
The q variable gets a copy of p. Modifying
q does not affect the original tuple.
$ dotnet run (10, 2) (10, 2) (10, 20)
C# tuple swap values
With tuples, we can easily swap values of two variables.
var (x, y) = (11, 2);
Console.WriteLine($"x: {x}");
Console.WriteLine($"y: {y}");
(y, x) = (x, y);
Console.WriteLine($"x: {x}");
Console.WriteLine($"y: {y}");
We define and initialize two variables x and y.
Later we swap their values in one line.
(y, x) = (x, y);
The right-hand tuple is created first from the current values; then it is deconstructed into the variables on the left. No temporary variable is needed.
$ dotnet run x: 11 y: 2 x: 2 y: 11
C# tuple deconstruction
We can unpack all the items in a tuple in a single operation. This is called tuple deconstruction.
var (name1, age1) = ("John Doe", 34);
Console.WriteLine($"{name1} is {age1} years old");
(string name2, int age2) = ("Roger Roe", 55);
Console.WriteLine($"{name2} is {age2} years old");
string name3;
int age3;
(name3, age3) = ("Jane Doe", 26);
Console.WriteLine($"{name3} is {age3} years old");
var (name4, _, occupation4) = ("Paul Smith", 41, "teacher");
Console.WriteLine($"{name4} is a {occupation4}");
The example shows how to deconstruct tuples.
var (name1, age1) = ("John Doe", 34);
The tuple field values are stored in the name1 and
age1 variables. We declare and assign the variables in one step.
(string name2, int age2) = ("Roger Roe", 55);
Here we explicitly specify the variable types.
string name3;
int age3;
(name3, age3) = ("Jane Doe", 26);
We can also deconstruct a tuple into existing variables.
var (name4, _, occupation4) = ("Paul Smith", 41, "teacher");
The underscore is a discard; it ignores a value we are not interested in.
$ dotnet run John Doe is 34 years old Roger Roe is 55 years old Jane Doe is 26 years old Paul Smith is a teacher
C# nested tuples
It is possible to nest tuples into other tuples.
var data = ((1, 2, 3), (3, 4, 5), (6, 7, 8)); Console.WriteLine(data); Console.WriteLine(data.Item1); Console.WriteLine(data.Item2); Console.WriteLine(data.Item3); Console.WriteLine(data.Item1.Item1); Console.WriteLine(data.Item2.Item3);
We have a tuple which has three nested tuples consisting of three values.
Console.WriteLine(data.Item1);
Here we get the first nested tuple: (1, 2, 3).
Console.WriteLine(data.Item1.Item1); Console.WriteLine(data.Item2.Item3);
Here we get the first element of the first nested tuple and the third element of the second nested tuple.
$ dotnet run ((1, 2, 3), (3, 4, 5), (6, 7, 8)) (1, 2, 3) (3, 4, 5) (6, 7, 8) 1 5
C# tuple equality
Since C# 7.3, tuples support the == and !=
operators. The tuples are compared element by element, in order.
var a = (Name: "John", Age: 34);
var b = (Name: "John", Age: 34);
var c = (First: "John", Years: 34);
Console.WriteLine(a == b);
Console.WriteLine(a == c);
Console.WriteLine(a != ("John", 35));
Console.WriteLine(a.Equals(b));
We compare tuples with the equality operators and the Equals
method.
Console.WriteLine(a == c);
The field names do not take part in the comparison; only the values matter.
The a and c tuples are therefore equal.
$ dotnet run True True True True
C# tuple as function parameter
Tuples can be function parameters.
ShowInfo(("John", "Doe", "programmer"));
ShowInfo(("Roger", "Roe", "driver"));
ShowInfo(("Jane", "Doe", "teacher"));
void ShowInfo((string FirstName, string LastName, string Occupation) u)
{
Console.WriteLine($"{u.FirstName} {u.LastName} is a {u.Occupation}");
}
We pass tuples to the ShowInfo function, which builds and prints
a message from their fields.
void ShowInfo((string FirstName, string LastName, string Occupation) u)
The parameter type lists the types and names of the tuple fields.
$ dotnet run John Doe is a programmer Roger Roe is a driver Jane Doe is a teacher
C# tuple as return value
Tuples are often used to return multiple values from a function. This
replaces out parameters or small helper classes.
int[] vals = [4, -2, 9, 0, 13, 7];
var stats = GetStats(vals);
Console.WriteLine($"min: {stats.Min}, max: {stats.Max}, avg: {stats.Avg:F2}");
var (min, max, _) = GetStats([1, 2, 3]);
Console.WriteLine($"min: {min}, max: {max}");
(int Min, int Max, double Avg) GetStats(int[] data)
{
return (data.Min(), data.Max(), data.Average());
}
The GetStats function computes the minimum, maximum, and average
of an array of integers.
(int Min, int Max, double Avg) GetStats(int[] data)
{
return (data.Min(), data.Max(), data.Average());
}
The return type is a tuple having three named fields.
var (min, max, _) = GetStats([1, 2, 3]);
The returned tuple can be deconstructed directly; we discard the average.
$ dotnet run min: -2, max: 13, avg: 5.17 min: 1, max: 3
C# tuples in collections
Tuples can be stored in collections and produced by LINQ queries.
var users = new List<(string Name, string Occupation, int Age)>
{
("John Doe", "gardener", 34),
("Roger Roe", "driver", 55),
("Jane Doe", "teacher", 26),
("Lucy Smith", "teacher", 41),
};
foreach (var (name, occupation, age) in users.Where(u => u.Age > 30))
{
Console.WriteLine($"{name}, {occupation}, {age}");
}
var distances = new Dictionary<(string, string), int>
{
[("Bratislava", "Vienna")] = 80,
[("Bratislava", "Budapest")] = 200,
};
Console.WriteLine(distances[("Bratislava", "Vienna")]);
foreach (var g in users.GroupBy(u => u.Occupation)
.Select(g => (Occupation: g.Key, Count: g.Count())))
{
Console.WriteLine($"{g.Occupation}: {g.Count}");
}
We have a list of users represented by tuples.
foreach (var (name, occupation, age) in users.Where(u => u.Age > 30))
The foreach statement can deconstruct each tuple into variables.
var distances = new Dictionary<(string, string), int>
Since value tuples implement equality and hash codes, they can serve as composite dictionary keys.
foreach (var g in users.GroupBy(u => u.Occupation)
.Select(g => (Occupation: g.Key, Count: g.Count())))
A LINQ projection creates a tuple with the occupation and the number of users in each group.
$ dotnet run John Doe, gardener, 34 Roger Roe, driver, 55 Lucy Smith, teacher, 41 80 gardener: 1 driver: 1 teacher: 2
C# tuple & pattern matching
A pattern is a syntactic form that can be used to express the shape of data against which incoming data is compared. Tuple patterns allow us to match several values at once.
(string, string)[] games =
[
("rock", "scissors"),
("paper", "rock"),
("scissors", "rock"),
("paper", "paper"),
];
foreach (var (p1, p2) in games)
{
Console.WriteLine($"player 1 has {p1}, player 2 has {p2}");
Console.WriteLine(RockPaperScissors(p1, p2));
}
string RockPaperScissors(string player1, string player2) => (player1, player2) switch
{
("rock", "paper") => "Rock is covered by paper. Player 2 wins.",
("rock", "scissors") => "Rock breaks scissors. Player 1 wins.",
("paper", "rock") => "Paper covers rock. Player 1 wins.",
("paper", "scissors") => "Paper is cut by scissors. Player 2 wins.",
("scissors", "rock") => "Scissors are broken by rock. Player 2 wins.",
("scissors", "paper") => "Scissors cut paper. Player 1 wins.",
(_, _) => "Tie."
};
This is a simple skeleton of a rock-paper-scissors game. We evaluate a few fixed games.
string RockPaperScissors(string player1, string player2) => (player1, player2) switch
{
("rock", "paper") => "Rock is covered by paper. Player 2 wins.",
("rock", "scissors") => "Rock breaks scissors. Player 1 wins.",
("paper", "rock") => "Paper covers rock. Player 1 wins.",
("paper", "scissors") => "Paper is cut by scissors. Player 2 wins.",
("scissors", "rock") => "Scissors are broken by rock. Player 2 wins.",
("scissors", "paper") => "Scissors cut paper. Player 1 wins.",
(_, _) => "Tie."
};
We use tuple patterns in a switch expression. The tuples represent choices of the two players. Each of the switch expression arms represents a possible scenario in the game. The last arm with two discards matches any remaining combination, which in this game is a tie.
$ dotnet run player 1 has rock, player 2 has scissors Rock breaks scissors. Player 1 wins. player 1 has paper, player 2 has rock Paper covers rock. Player 1 wins. player 1 has scissors, player 2 has rock Scissors are broken by rock. Player 2 wins. player 1 has paper, player 2 has paper Tie.
Source
Tuple types - language reference
In this article we have worked with tuples in C#.
Author
List all C# tutorials.