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
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.
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.
- What is this Directory.Packages.props file all about? - January 25, 2024
- How to add Tailwind CSS to Blazor website - November 20, 2023
- How to deploy a Blazor site to Netlify - November 17, 2023