Skip to footer content
Iron Academy Logo
Learn C#
Learn C#

Other Categories

Intro to Tuples in C# In 10 Minutes or Less

Tim Corey
9m 49s

C# methods are designed around a single return value, which works until you hit a case where one return is genuinely not enough. Address validation is the classic example: you need to know whether the input is valid, and if it is, you want the cleaned-up canonical form back. Wrapping that pair in a brand new class feels heavy for one method, and out parameters mix input and output in a way that ages badly. Tuples were added to the language so this exact situation has a cleaner answer.

In his video "Intro to Tuples in C# In 10 Minutes or Less," Tim Corey works through tuples by building a small address validation method that needs to return both a formatted string and a validity flag. Along the way he covers the syntax for returning a tuple, why naming the members matters, how to split the result into separate variables at the call site, and how to skip parts you do not care about with the discard character. Readers who still reach for one-off result classes or out parameters will find a lighter pattern here.

The Problem: Returning More Than One Value

[0:18 - 2:50] Tim opens with a ValidateAddress method that accepts a street address string. The behavior he wants is straightforward: if the input matches "123 Sesame St", return the properly formatted "123 Sesame Street"; otherwise, signal that the address is invalid. Two pieces of information need to come back from a single call, the cleaned-up string and the success flag.

The pre-tuple options are both awkward. Creating a ValidateAddressResult class works, but it leaves a one-purpose type sitting in the project that no other code will ever instantiate. Using an out parameter to send the formatted address back through the parameter list works too, but it mixes input and output in the method signature and makes the call site harder to read. Tuples were introduced to handle this case without paying either cost.

Returning an Unnamed Tuple

[2:50 - 4:01] The minimum syntax for a tuple return type wraps the two types in parentheses:

public (string, bool) ValidateAddress(string address)
{
    if (address == "123 Sesame St")
    {
        return ("123 Sesame Street", true);
    }
    return (address, false);
}
public (string, bool) ValidateAddress(string address)
{
    if (address == "123 Sesame St")
    {
        return ("123 Sesame Street", true);
    }
    return (address, false);
}

The method now returns a (string, bool) pair. The downside surfaces immediately when something tries to use it. With no names assigned to the members, IntelliSense exposes them as Item1 and Item2, which tells the next reader nothing about what each value represents. Tim flags this as the reason he never ships unnamed tuples even though the syntax is the shortest. The cost of reading result.Item2 six months later is higher than the cost of typing a name once.

Naming the Tuple Members

[4:01 - 6:30] Assigning names to each member of the tuple is a single-line change to the return type declaration:

public (string Address, bool IsValid) ValidateAddress(string address)
{
    if (address == "123 Sesame St")
    {
        return ("123 Sesame Street", true);
    }
    return (address, false);
}
public (string Address, bool IsValid) ValidateAddress(string address)
{
    if (address == "123 Sesame St")
    {
        return ("123 Sesame Street", true);
    }
    return (address, false);
}

Now the call site reads naturally:

var result = ValidateAddress("123 Sesame St");

if (result.IsValid)
{
    Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
    Console.WriteLine("That is an invalid address");
}
var result = ValidateAddress("123 Sesame St");

if (result.IsValid)
{
    Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
    Console.WriteLine("That is an invalid address");
}

Running the code with "123 Sesame St" prints the canonical form. Knocking off the leading 1 to produce "23 Sesame St" makes IsValid come back as false and prints the invalid-address message. The contract of the method is now self-documenting at the call site without a named tuple requiring a supporting class file.

A tuple can carry more than two members. Three, four, or five work the same way. Tim notes that once a tuple grows past three or four members, a record or class usually becomes the clearer choice, but for two or three related values from a single method, the tuple stays the lighter option.

Deconstructing the Tuple Into Separate Variables

[6:30 - 7:39] Sometimes the caller would rather hold each piece of the result in its own variable instead of dotting into a single result. C# supports that directly with deconstruction syntax:

(string message, bool valid) = ValidateAddress("123 Sesame St");

if (valid)
{
    Console.WriteLine($"Your validated address is {message}");
}
(string message, bool valid) = ValidateAddress("123 Sesame St");

if (valid)
{
    Console.WriteLine($"Your validated address is {message}");
}

The variable names on the left side do not have to match the tuple member names on the method. Here message replaces Address and valid replaces IsValid. Tim points out that this is a deconstructor, not a destructor; the two words sound similar but refer to entirely different concepts. The deconstructor splits the tuple into the local variables the caller actually wants.

Skipping Values with the Discard Character

[7:41 - 8:54] When the caller only cares about a subset of the returned values, the discard character (_) drops the ones they do not need:

(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);
(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);

The underscore tells the compiler "I am ignoring this value on purpose." This is more expressive than assigning the unwanted member to a throwaway variable name like unused, because a discard communicates intent. Anyone reading the line knows that ignoring IsValid is a deliberate choice for this call site, not an oversight.

Wrapping Up: When Tuples Earn Their Place

[8:54 - 9:28] The primary use case for tuples is returning multiple values from a method without creating a class that exists only to be returned once. Naming the members at the method signature, deconstructing them at the call site, and discarding the ones the caller does not need are the three patterns that make tuples comfortable to live with. Beyond that, tuples are useful in LINQ projections and intermediate calculations where a quick paired value is more readable than an anonymous type.

Conclusion

[9:28 - 9:49] Tuples give C# a compact way to return more than one value from a method. The named form is the version worth using day to day, because result.Address reads while result.Item1 does not. Deconstruction promotes the parts back to local variables when the caller prefers that shape, and the discard character lets a single call take only what it needs.

Example Tip: When you start deconstructing the same tuple shape in three or more places, that is the signal to graduate to a record instead. Tuples shine when the shape is local to one method; the moment the shape becomes part of your domain, a named type pays for itself.

Watch full video on his YouTube Channel and gain more insights on writing modern, expressive C# in the 10-Minute Training series.

Hero Worlddot related to Intro to Tuples in C# In 10 Minutes or Less
Hero Affiliate related to Intro to Tuples in C# In 10 Minutes or Less

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me