Tuple & ValueTuple (a data structure consisting of multiple parts)
Tuple added in .NET4 and even better ValueTuple added in .NET4.7 can be very useful and need less coding.
Who remembers writing classes like this?
internal class Person
{
internal string Firstname { get; set; }
internal string Surname { get; set; }
internal int Age { get; set; }
}
and then using them like this;
var person = new Person()
{
Firstname = "Dominic",
Surname = "Batstsone",
Age = 500 //live forever?
};
that's cool, nothing really wrong there. We've been doing this kind of thing for years. When .NET4 came out Tuple<T> was introduced with all the other lovely goodness that Generics gave (although I'll probably write about Generics seperately because it's a big topic).
NET4 was available to play around with from around May 2009 and it's now December 2019 - more than a decade ago. Don't feel bad if you don't already use Tuples. A straw poll of my old colleagues reveals that they have not really used them either. This really shows how being stuck on legacy applications can really stifle your skills. Anyway - time for learning!
Common Uses of Tuples
To represent a single set of data. For example, a tuple can represent the Person class defined at the start of this post, or a database record, and its components can represent individual fields of the record.
To return multiple values from a method without the use of out
parameters (in C#) or ByRef
parameters (in Visual Basic). It's fairly dirty to do that as it's almost like a hack. Using a Tuple fixes this by allowing one return value that contains many values.
To pass multiple values to a method through a single parameter. Passing a load of values is messy and unweildy. A Tuple makes for far more readable code and sometimes the only way to pass multiple values to things you have no control over. For example, the Thread.Start(Object) method has a single parameter that lets you supply one value to the method that the thread executes at startup. If you supply a Tuple<T1,T2,T3,T4,T5,T6,T7> object as the method argument, you can supply the thread's startup routine with seven items of data.
(The above is from MS Docs and I added bits).
Tuple<T>
Ok, so here's the definition from Microsoft Docs.
[System.Serializable]
public class Tuple<T1,T2,T3,T4,T5,T6,T7,TRest> : IComparable, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable
As you can see, you can add up to 8 elements. The final element can be another Tuple which means you can link them together. This should be used with caution really, because you could quite easily end up with speghetti code or code that no-one else understands!
It is important to note that Tuples are Reference Types like classes, so multiple things can reference them. Tuples are imutable which means the properties are read-only.
You can create a Tuple like this:
var personTuple = new Tuple<string, string, int>("Dominc", "Batstone", 500);
The example above explicitly states the datatypes, but they will be known if you create a tuple like this using the Create method. This is a little shorter and nicer to read!
var personTuple2 = Tuple.Create("Dominc", "Batstone", 500);
You access the values of a tuple like this. This shows their limitation because they cant be given named properties, so the readability & maintainability of your code will be more difficult. This is a bit like the anti-pattern magic numbers. You might know what it is now, but what about in a month or year.
Console.WriteLine(personTuple.Item1);//returns Dominic
Tuple is a bit like an Anonymous Type, but different!
So far, we have seen that a Tuple is a little like an anonymous type, they are both immutable, they are both reference types. Anonymous types can have property names which is an advantage. Tuples can be used as return values, although so can anonymous types but see Jon Skeet's Articl about it.
ValueTuple<T>
Ok, so what is this? Well a ValueTupe is like a Tuple but it is a value type. Because this time it can have named properties, it is similar to a struct.
By the way, ValueTuple wasn't introduced until .NET4.7 - so ages after Tuple! Something I just read in the docs is
Serialization and value tuples
The ValueTuple type is not serializable in .NET Core 1.x or in the .NET Framework 4.7 and earlier versions. In addition, .NET Standard, including .NET Standard 2.0, does not mandate serialization of ValueTuple instances; whether or not a ValueTuple instance is serializable depends on the individual .NET Standard implementation. To determine whether a ValueTuple type is serializable on a particular .NET implementation, get a Type object that represents the ValueTuple type and retrieve the value of its IsSerializable property.
There are loads of ways to create a ValueTuple. Look at the last two of these, awesome!!!
You can still access elements by using the .ItemN where N is 1-8, but it;s much more readable if you actually use the named elements. Even IntelliSene will show you how;
Deconstruction (into individual elements)
This is quite cool; you can deconstruct into individual named elements like this
(string name, string last, int age) = personValueTuple;
Console.WriteLine($"The name element is {name}");
Performance
I won't go too much into performance, but just bear in mind that one is a referenec type and the other is a value type. Keep element sizes small, otherwise when you're using ValueTuple you might be copying data around. Likewise for Tuple there might be a Garabage Collection hit. There is an interesting article for further reading here which you might want to have a look at.
Conclusion
Tuple and ValueTuple provide a nice way to pass and return data without writing classes (like the one at the top). Care needs to be taken to ensure that
- The data itself remains small, especially in ValueTuple.
- Code remains readable & maintainable (e.g. avoid "magic numbers")
- Consider each situation - would a structure, class or anonymous type be more appropriate?
This is part of my personal refresh of C# series of blog posts.