Skip to content

C# Tuples

Reading Mode

In the C# 7.0 language specification added additional capability for working with tuples.

A tuple is essentially a single data structure that consists of a number of parts or elements.

The Tuple class was available prior to C# 7.0 and its usage was sometimes clumsy. The individual items in a tuple can be of differing types.

In this post we will explore what Tuples are and why they can be useful when developing applications using C# 7.0

I have previously posted about another exciting and useful C# language enhancements , C# local functions. While creating the code samples for the post I really wanted to include code samples with C# Tuples, but for the sake of brevity and not to over complicate my contrived samples decided to write this separate post.

What are C# Tuples

A tuple is a finite ordered list of elements.
In mathematics, an n-tuple is a sequence (or ordered list) of n elements, where n is a non-negative integer. There is only one 0-tuple, an empty sequence. An n-tuple is defined inductively using the construction of an ordered pair.

Wikipedia

Tuples are a popular construct in most functional programming languages such as F#, Haskell, Go, Scala etc. Primarily because it is useful to compose functions directly with other functions that accept tuples. Values are often bundled into one unit conceptually and developers are able pass them all together.

Advantages of tuples

  • Tuples are finite sized in nature i.e. we can not add/delete elements to/from a tuple.
  • Ability to search any element in a tuple.
  • faster than lists, due to they are constant set of values.
  • Tuples can be used as dictionary keys as they contain immutable values

Why tuples in C# ?

There are many instances during development when you need to pass a lists values to functions for processing.

Typically developers will tackle this problem by either creating KeyValuePair<TKey,TValue>, Generic List List Class, Collections or even custom classes. In most cases, those approaches have and will continue to more than satisfactory or even still the most optimum solution.

Some of you reading this may be thinking, tuples aren't anything really new, after all we've had the Tuple Class since .Net Framework 4 .

The Tuple Class was not really a C# concept, rather they are .NET types and in reality fall pretty short from being the true tuples. They were limited due in part that the members had fixed names and never really communicated their purpose. They were also reference types which had significant impact on performance besides the fact that they had no syntactical support within C#.

In C#7, tuples have been added to language to provide a concise way to group values together without having to explicitly define specific types.

Using Tuples

In C#7 tuples have been added to the language, and you can use tuple definitions as types to generics making use of the System.ValueTuple , providing first class syntax support.

//Define a tuple using parantheses
var freelancer = ("Gary", "Woodfine");
 
Console.WriteLine ( freelancer.item1 + ' ' + freelancer.item2);
 
// named tuple
var freelancer = ( firstName : "Gary", lastName : "Woodfine" );
 
// Tuple defined variable
(string firstName, string lastName) freelancer = ("Gary" , "Woodfine");
 
// Either method of defining the tuple we could access the item as:
Console.WriteLine(freelancer.firstName + ' ' + freelancer.lastName);
 
// it is also possible to define a tuple with naming it. Basically defining 2 local variables
// to use in the rest of the code
         (string firstName ,string lastName) = ("Gary", "Woodfine");
 
            Console.WriteLine(firstName + ' ' + lastName);

Tuples are best for internal and private methods rather than public APIs where a static type is much more helpful to the consumer of your API.

Tuple as a return type

You can stipulate a Tuple as return type for a method

static void Main(string[] args)
        {
           var freelancer = GetFreelancer();
 
            Console.WriteLine(freelancer.firstName + ' ' + freelancer.lastName);
 
        }
 
        static (string firstName, string lastName) GetFreelancer()
        {
            return ("Gary", "Woodfine");
 
        }

Serialization Concerns

Tuples in C#7 are limited serialization capabilities.

Attempting to serialise our tuple to JSON format

static void Main(string[] args)
        {
            var freelancer = GetFreelancer();
            Console.WriteLine(JsonConvert.SerializeObject(freelancer));
        }
 
        static (string firstName, string lastName) GetFreelancer()
        {
            return ("Gary", "Woodfine");
 
        }

Will result in {"Item1":"Gary","Item2":"Woodfine"} being returned. You'll notice the Keys have been removed.

How the C# 7 ValueTuple type works

In order to understand this behaviour we need to simplify and decompile our code.

public class UsernameFactoryTests
{
   private UsernameFactory _factory;

   public UsernameFactoryTests()
   {
      _factory = new UsernameFactory();
   }

   [Fact]
   public void ShouldGetFirstNameFirst()
   {
       //arrange
        var user = "Gary Woodfine";
           
        //act
        var username = _factory.GetUserName(user);

        //assert
        Assert.Equal("Gary", username.FirstName);
        Assert.Equal("Woodfine", username.LastName);

   }

   [Fact]
   public void ShouldGetLastNameFirst()
   {
      //arrange
      var user = "Woodfine, Gary";

      //act
      var username = _factory.GetUserName(user);

      //assert
      Assert.Equal("Gary", username.FirstName);
      Assert.Equal("Woodfine", username.LastName);

   }
 }

You'll notice that the de-compiled, the GetFreelancer method is simply syntactic sugar and named elements have been pulled out and added via the TupleElementNames attribute. This is because the ValueTuple type's named elements are erased at runtime, meaning there's no runtime representation of them.

public class UsernameFactoryTests
{
   private UsernameFactory _factory;

   public UsernameFactoryTests()
   {
      _factory = new UsernameFactory();
   }

   [Fact]
   public void ShouldGetFirstNameFirst()
   {
       //arrange
        var user = "Gary Woodfine";
           
        //act
        var username = _factory.GetUserName(user);

        //assert
        Assert.Equal("Gary", username.FirstName);
        Assert.Equal("Woodfine", username.LastName);

   }

   [Fact]
   public void ShouldGetLastNameFirst()
   {
      //arrange
      var user = "Woodfine, Gary";

      //act
      var username = _factory.GetUserName(user);

      //assert
      Assert.Equal("Gary", username.FirstName);
      Assert.Equal("Woodfine", username.LastName);

   }
 }

No Reflection

The absence of named elements in the compiled source means that it's not possible to use reflection to get those name elements via reflection, which limits ValueTuple's utility.

This is because under the bonnet the compiler is erasing the named elements and reverting to the Item1 and Item2 properties, meaning our serialiser doesn't have access to the properties.

Conclusion

Tuples are a great addition to the C# language and can be extremely useful. However, as with any extension or enhancement to software development languages it is vitally important to understand both the advantages and disadvantages in order to make use of them.

Gary Woodfine
Latest posts by Gary Woodfine (see all)