In this article, we are going to take an in-depth look at the difference between ValueTuple and Tuple. We are also going to look at extra features that came with ValueTuples and also some potential drawbacks.
Let’s start.
What is a ValueTuple in C#?
ValueTuple in C# is a value-type data structure that allows the storage of items of different data types. It is simply a value type representation of the Tuple class in C#.
The .Net framework 4.7 introduced ValueTuple as a feature of C# 7.0.
Let’s take a look at why exactly we need the ValueTuple.
Why Do We Need ValueTuples in C#?
The Tuple class though useful has numerous drawbacks that make it difficult to work with. The sole reason we need the ValueTuple is that it gives us features that greatly improve the limitations of Tuples. The ValueTuple greatly reduces complexity and improves code optimization.
Let’s start by looking at features of ValueTuple that make it an improvement of the Tuple class.
The Data-Type Difference Between ValueTuple and Tuple
A major problem with the tuple class is that it is a reference-type with memory allocation on the heap. Due to allocations and garbage collection pressure, using it can lead to CPU-intensive operations and major performance issues in our system. The ValueTuple on the other hand is a lightweight value type object and has its memory stored on the stack. This means that with ValueTuples we can build better-optimized systems.
Unlike the tuple, ValueTuple is a mutable struct and exposes its elements as fields. We can also create a ValueTuple with no elements.
In terms of performance, flexibility, and reusability the ValueTuple is better.
Initialization Difference Between ValueTuple and Tuple
Just like the Tuple class, we can create ValueTuple using the constructor syntax and the static Create
method. The ValueTuple struct also comes with an additional means of initialization – Using Parentheses.
Creating A ValueTuple Using Parentheses in C#
This is the easiest way to create a ValueTuple. It involves specifying the elements that we store in a comma-separated list within a set of parenthesis:
(int, int) initializedValueTupleOne = (1, 2);
This is the convention when creating ValueTuples with unnamed parameters. Additionally, we can simplify the code:
var initializedValueTupleTwo = (1, 2);
The Tuple class does not support this kind of initialization.
A problem with Tuple is that there is no way to rename its elements to better describe their value. When we use ValueTuples with unnamed parameters we face the exact same limitation. To access the elements of initializedValueTupleOne
, we would have to call initializedValueTupleOne.Item1
or initializedValueTupleOne.Item2
. This type of syntax is inconvenient especially when we have multiple elements or elements with similar data types.
ValueTuple solves this problem by giving us the ability to have named parameters:
(int lastYear, int currentYear) namedValueTupleOne = (2021, 2022);
When we use named parameters with ValueTuples, it reduces complexity and makes our code more readable. Now, we can access the elements with their custom names:
var currentYear = namedValueTupleOne.currentYear; //returns 2022
We can also give names to our parameters from the right-hand side of our code declaration:
var rightHandNamedValueTuple = (language: "C#", tool: "Laptop", occupation: "Developer");
If we try to assign names to our parameters from both sides of our code declaration:
(string lang, string machine, string career) rightHandNamedValueTupleTwo = (language: "C#", tool: "Laptop", occupation: "Developer");
The declaration on the left-hand side will overrule the declaration on the right side. So let’s stick to naming our parameters from one side of our declaration.
A variable with a minimum of two elements is a ValueTuple. For example, this is not a ValueTuple:
var number = (12);
Creating a ValueTuple Using The Create Method in C#
The ValueTuple struct contains a static Create method which returns a ValueTuple:
var initializedValueTupleFour = ValueTuple.Create("code-maze", "johndoe", "dotnet");
For the sake of comparison, this is the equivalent Tuple declaration:
var initializedTupleThree = Tuple.Create("code-maze", "johndoe", "dotnet");
Using this syntax, we can initialize a ValueTuple with no elements:
var valueTupleWithNoElementsOne = ValueTuple.Create();
Creating a ValueTuple using Class-based Syntax in C#
By using the constructor, we can create a ValueTuple instance:
var valueTupleWithNoElements = new ValueTuple(); var initializedValueTupleSix = new ValueTuple<decimal, decimal, decimal>(10000M, 20000M, 30000M);
In this example, valueTupleWithNoElements
is a ValueTuple with no elements in it.
The Difference in Storage Capacity Between ValueTuple and Tuple
Tuples can hold a maximum of eight elements. When we want to include more elements, we have to use nested tuples:
var nestedTuple = Tuple.Create("my", "name", "is", "john", "doe", "and", "I", Tuple.Create("am", 1000, "years", "old", true));
ValueTuples on the other hand can hold over eight items:
var valueTuple = ("my", "name", "is", "john", "doe", "and", "I", "am", 1000, "years", "old", true);
We can also assign variables as member values:
var age = 999; var name = "metuse"; var gender = "male"; var occupation = "pilot"; var hobby = "anime watching"; var mail = "[email protected]"; var bestColor = "blue"; var weight = "1000kg"; var profile = (age, name, gender, occupation, hobby, mail, bestColor, weight);
The Difference in Accessibility Between ValueTuple and Tuple
In the Creating A ValueTuple Using Parentheses in C# section, we talked about how we are restricted with Tuples and unnamed ValueTuples to accessing their elements only with Item1 and Item2. Additionally, we’ve explained how named ValueTuples solve that restriction.
Another difference is when we want to access the last element of a ValueTuple versus the same of a Tuple if it has nested elements. To access the nested element of a Tuple, we have to use the Rest
property:
var evenNumbers = Tuple.Create(2, 4, 6, 8, 10, 12, 14, Tuple.Create(16, 18, 20, 22, 24, 26)); var evenNumbersRestItem = evenNumbers.Rest.Item1; //returns (16, 18, 20, 22, 24, 26) var evenNumberRestItemElementOne = evenNumbers.Rest.Item1.Item1; //returns 16
For ValueTuple, we don’t need the Rest
property because we don’t have to nest any element:
var evenNumbers2 =(one: 2, two: 4, three: 6, four: 8,five: 10, six: 12, seven: 14, eight:16, nine:18, ten:20); var itemTwelve = evenNumbers2.nine; // returns 18
Method Return Type Difference Between ValueTuple and Tuple
We can use ValueTuple/Tuple when we need to return multiple items from a method.
For ValueTuple:
public static (bool isDay, string greeting) ReturnAValueTuple() { if (DateTime.Now.Hour < 12) return (isDay: true, greeting: "Good Morning"); return (isDay: false, greeting: "Good Evening"); }
The Tuple equivalent of the above code:
public static Tuple<bool,string> ReturnATuple() { if (DateTime.Now.Hour < 12) return Tuple.Create(true, "Good morning"); return Tuple.Create(false, "Good Evening"); }
After we execute the first method, the result will have named parameters, but with the second one, we are tied to Item1 and Item2.
Using a ValueTuple As a Method Parameter
We can pass multiple values to a method as a single unit:
public static string TakeInAValueTuple((int legCount, bool isBlue) organism) { var hasAtLeastFourLegs = organism.legCount >= 4; var isBlue = organism.isBlue; return $"hasAtLeastFourLegs : {hasAtLeastFourLegs} and isBlue : {isBlue}"; }
The TakeInAValueTuple
method takes two named parameters as a single ValueTuple and returns a string.
Let’s look at the same code but with Tuples:
public static string TakeInATuple(Tuple<int, bool> organism) { var hasAtLeastFourLegs = organism.Item1 >= 4; var isBlue = organism.Item2; return $"hasAtLeastFourLegs : {hasAtLeastFourLegs} and isBlue : {isBlue}"; }
We have looked at the features of ValueTuple that make it an improvement of the Tuple class. To continue, let’s look at some features that are specific to the ValueTuple only.
Deconstruction of ValueTuple in C#
We’ve seen how we can return multiple items from a method using ValueTuples:
public static (bool isLengthEven, string message, int stringLength) CheckStringLength(string word) { var stringLength = word.Length; var isLengthEven = stringLength % 2 == 0; var message = isLengthEven ? "This is even" : "This is odd"; return (isLengthEven, message, stringLength); }
The CheckStringLength
method returns a three-member ValueTuple and we can use the dot notation to access the elements of its result:
var result = CheckStringLength("October"); var isLengthEven = result.isLengthEven; var message = result.message; var stringLength = result.stringLength;
With deconstruction, we can do this in one line of code. We use deconstruction when we want to break down a variable (like a ValueTuple) into its constituent elements and store each of those elements in new variables:
var (isLengthEvenn, messagee, stringLengthh) = CheckStringLength("October");
We can also use discards in place of elements that we don’t need:
var (_, message2, _) = CheckStringLength("January");
Change The Value of the Elements of a ValueTuple in C#
After creation, a Tuple is read-only. We cannot edit its elements or their values.
ValueTuple on the other hand is a mutable struct. We can reassign the values of the elements in a ValueTuple:
(string satellite, string star, string planet, string galaxy) heavenlyBodies = ("satellite", "star", "planet", "galaxy"); heavenlyBodies.planet = "earth"; heavenlyBodies.galaxy = "milky way"; heavenlyBodies.star = "antares"; heavenlyBodies.satellite = "moon";
Checking For Equality in ValueTuples
Internally, the ValueTuple is a struct that implements IEquatable, this means that we can compare ValueTuples using the equality operator:
var valueTupleOne = (1, 3, 6); var valueTupleTwo = (2, 3, 7); var isEqual = valueTupleOne == valueTupleTwo;
We can do this directly as well:
var isEqualTwo = (1, 3, 6) == (2, 3, 7);
Converting a Tuple To a ValueTuple
We can easily convert a Tuple to a ValueTuple using the ToValueTuple
method:
var tuple = new Tuple<int, string>(24, "john doe"); var tupleConvertedToValueTuple = tuple.ToValueTuple();
The only perk here is that converting a Tuple to a ValueTuple will return an unnamed ValueTuple, once again restricting us to the Item1, Item2 syntax.
In the same light, we can also convert a ValueTuple into a Tuple:
var valueTuple = (24, "john-doe"); var valueTupleConvertedToTuple = valueTuple.ToTuple();
We’ve looked at various reasons why the ValueTuple is unique. Let’s check out some potential drawbacks that could come with using the ValueTuple.
Limitations of The ValueTuple
It is worthy to note that the named-parameter feature of ValueTuples is just syntactic sugar to help with the readability and simplicity of our code. Named parameters do not have a runtime representation, which means that under the hood, the C# compiler is going to revert the custom names we have given the elements to Item1, Item2, etc.
This has two main implications. First, we cannot access our named parameters via reflection. Secondly, if we cast a ValueTuple with named parameters to a dynamic type, trying to access the result elements using their custom names is going to give us a RuntimeBinderException because the system does not recognize those names anymore.
Conclusion
Even with its limiting factors, the ValueTuple is a significant improvement of the Tuple class. In this article, we learned the different ways in which the ValueTuples differ from the Tuple. We also looked at extra features that beautify the ValueTuple and finally, the limitations that come with it.