C# – Working with tuples

Here’s how you create a tuple:

var philosopherTuple = (name: "Seneca", philosophy: "Stoic"); Console.WriteLine(philosopherTuple.name);
Code language: C# (cs)

Tuples are containers for two or more variables. Without tuples, you’d have to use a class/struct, like this:

public class Philosopher { public string Name { get; set; } public string Philosophy { get; set; } } var philospher = new Philosopher() { Name = "Seneca", Philosophy = "Stoic" }; Console.WriteLine(philosopher.name);
Code language: C# (cs)

In other words, tuples provide a convenient alternative to class/structs. Instead of having tons of data container classes, you can use tuples.

In this article, I’ll show examples of how to use tuples in practical ways.

Creating a tuple

Tuples are now fully integrated in C#. They used to be clunky. Now they’re simple and clean.

There are two main ways to create tuples:

  • Declare the tuple, then assign each field.
(int f, int c, double humidity, DateTime time) temperature; temperature.f = 28; temperature.c = -2; temperature.humidity = 73.0; temperature.time = DateTime.Now;
Code language: C# (cs)
  • In-line declaration and setting the values.
var temperature = (f: 28, c: -2, humidity: 73.0, time: DateTime.Now);
Code language: C# (cs)

The default tuple field names are terrible. If you don’t specify the field names, the defaults are “Item1”, “Item2”, and so on. It’s a very good idea to specify the field names (but they are optional, so leave them out if you want).

Assigning to multiple variables at once

Tuples aren’t just data containers. They also provide a way to assign multiple variables at once on a single line.

Let’s say you have the following data container class:

public class Temperature { public double F { get; set; } public double C { get; set; } public double Humidity { get; set; } public DateTime Time { get; set; } }
Code language: C# (cs)

You can use tuple unpacking to assign values to all four properties at once:

var temperature = new Temperature(); (temperature.C, temperature.F, temperature.Humidity, temperature.Time) = (-2, 28, 73.0, DateTime.Now);
Code language: C# (cs)

Returning a tuple from a method

Tuples are just like any other type. You can return them from methods.

Let’s say you have code that is reading data from a weather sensor. It’s passing sensor data in a single string, and you want to parse it out into fields, so that further processing is simpler.

Here’s how you create a method that returns a tuple:

static void Main(string[] args) { var temperature = ParseTemperatureData("-2:28:73.0"); Console.WriteLine(temperature.c); } static (double f, double c, double humidity, DateTime time) ParseTemperatureData(string data) { var a = data.Split(":"); return (f: double.Parse(a[0]), c: double.Parse(a[1]), humidity: double.Parse(a[2]), time: DateTime.Now); }
Code language: C# (cs)

Using a dictionary with tuples

You can put tuples in any data structure (maps, sets, lists). This section shows how to use a dictionary of tuples.

Let’s say you have a mapper class that gets JSON, deserializes it, then has to map a single field to multiple fields (demuxing). It’s using a switch statement to demux:

public class PaymentMapper { public Payment Map(string jsonResponse) { var paymentContract = JsonSerializer.Deserialize<PaymentContract>(jsonResponse); var payment = new Payment() { Amount = paymentContract.Amount }; switch (paymentContract.Code) { case "1a": payment.Code = Codes.Approved; payment.Text = "Payment was approved"; break; case "2b": payment.Code = Codes.Declined; payment.Text = "Payment was declined"; break; case "ra": payment.Code = Codes.RedAlert; payment.Text = "Fraud - call the police"; break; default: payment.Code = Codes.Unknown; break; } return payment; } } public enum Codes { Unknown, Approved, Declined, RedAlert } public class PaymentContract { public decimal Amount { get; set; } public string Code { get; set; } } public class Payment { public decimal Amount { get; set; } public Codes Code { get; set; } public string Text { get; set; } }
Code language: C# (cs)

You can replace the demuxing switch statement with a dictionary of tuples, like this:

public class PaymentMapper { private static readonly Dictionary<string, (Codes code, string text)> codeMap = new Dictionary<string, (Codes code, string text)>() { ["1a"] = (Codes.Approved, "Payment was approved"), ["2b"] = (Codes.Declined, "Payment was declined"), ["ra"] = (Codes.RedAlert, "Fraud - call the policy") }; private (Codes code, string text) DEFAULT_CODE = (Codes.Unknown, ""); public Payment Map(string jsonResponse) { var paymentContract = JsonSerializer.Deserialize<PaymentContract>(jsonResponse); var payment = new Payment() { Amount = paymentContract.Amount }; (payment.Code, payment.Text) = codeMap.GetValueOrDefault(paymentContract.Code, DEFAULT_CODE); return payment; } }
Code language: C# (cs)

JSON serialization doesn’t work with tuples

As of this writing, System.Text.Json doesn’t support serializing tuples. When you try to serialize a tuple, it returns an empty JSON string.

Newtonsoft.JSON also doesn’t work correctly with tuples:

var philosopherTuple = (name: "Seneca", philosophy: "Stoic"); var json = Newtonsoft.Json.JsonConvert.SerializeObject(philosopherTuple); Console.WriteLine(json);
Code language: C# (cs)

Here’s what this outputs:

Code language: plaintext (plaintext)

Notice that it’s not using the names specified in the tuple declaration (name and philosophy). Instead, it’s using Item1 and Item2, which is what you would get if you didn’t specify the names at all.

Does deserialization work? Let’s check:

var senecaTuple = Newtonsoft.Json.JsonConvert.DeserializeObject<(string name, string philosophy)>(json); Console.WriteLine(senecaTuple.name);
Code language: C# (cs)

This outputs the following:

Code language: plaintext (plaintext)

When deserializing, it matches the Item1 key to the first property in the tuple, and Item2 to the second property, and so on. It completely ignores the declared names in the tuple.

In other words, it’s not able to deserialize the following JSON into the tuple properly:

{ "name": "Seneca", "philosophy": "Stoic" }
Code language: JSON / JSON with Comments (json)

Trying to deserialize this into a tuple gives me a tuple with nulls. This because Newtonsoft.JSON is looking for properties in the JSON called Item1 and Item2.

Word of advice: Avoid JSON serialization with tuples for now.

Leave a Comment