Tuples, tuples... value tuples!

Search for a command to run...

No comments yet. Be the first to comment.
Just a very short blog post... for everyone who is not yet known to it. Since .Net Framework 4.6 for two immutable types we got new static fields in the framework: Array.Empty<T> string.Empty // instead of string foo = ""; byte[] bar = new byte[0...

Introduction In one of my older projects, I often had to synchronize and work with different parallel threads. Using async, parallel or 'real' threads does not make a real difference when it is coming to thread safety. As a very short short instructi...

In part III we take a closer look at where to upgrade our UserInterface from WindowsForms. As NyxSql was created as a learning project more than a decade ago, there is no MVVM or anything else implemented, which will make the upgrade more hard as the...

When using package manager with sources which require to authenticate yourself (e. g. internal sources or paid subscriptions), it may happen that you need to change your password or just entered the wrong credentials. Now, it is kinda like 'evil', be...

When working with multiple workstations (workstation / laptop) due to pandemic, we suddenly experienced the following error when opening solutions: Current solution contains incorrect configurations mappings. It may cause projects to not work correct...

In the very first beginning of tuples in c# I firstly skipped it, because the readability had been not what I though of creating readable code.
Example from MSDN:
(bool, string) t1 = (true, "");
Console.WriteLine($"Tuple with value {t1.Item1} and string {t1.Item2}.");
When using tuples with more than 2 variables it was always about remembering which variable is which item.
With the introduction of ValueTuples this changed. Now it is possible to assign a 'name' to each tuple variable:
(bool rc, string error) t2 = (true, "");
Console.WriteLine($"Tuple with value {t2.rc} and string {t2.error}.");
Now where should I use tuples?
In general they can be used everywhere. However imho for complex data, the code readability will be worse, if you use it for storing big data instead of using classes. Let me show you an example:
(string FirstName, string LastName, DateTime BirthDay) person
Using it together with a list will result in:
List<(string FirstName, string LastName, DateTime BirthDay) person>
In this case using a dedicated data class should be prefered as the readability greatly improves and the data class can be extended with additional methods.
But for temporarily storing small data constructs, where Dictionaries, Collections or Hastables do not bring in any benefit, they are great also!
That is the point where we switched to value tuples and now using them heavily. Instead of using out variables or ref variables for providing the error messages to the calling method we switched to tuples. Please note, that this is specific about requirements of returning an error - it is NOT a recommendation of when to use return values or Exceptions. See for example language agnostic - Which, and why, do you prefer Exceptions or Return codes? - Stack Overflow
Previously:
string error = "";
bool rc = FirstMethod(ref string error);
if(!rc)
_logger.Error(error);
rc = SecondMethod(ref string error);
if(!rc)
_logger.Error(error);
Now with tuples:
(bool success, string error) callRc = FirstMethod();
if(!callRc.success)
_logger.Error(callRc.error);
callRc = SecondMethod();
if(!callRc.success)
_logger.Error(callRc.error);
It can be easily extended with a more complex tuple for also handling an additional variable providing e. g. data from the called method which does work great together with nullable:
(bool success, string error, string[] lines) callRc = LoadFile();
if(!callRc.success)
{
_logger.Error(callRc.error);
return;
}
// process data using callRc.lines ...
Instead of specifying the return values each on it's own:
(bool success, string error) callRc = FirstMethod();
We can also use var which does work perfect with IntelliSense:
var callRc = FirstMethod();
And last but not least, we can also 'desconstruct' the tuple and direct access the single variables:
(bool success, string error) = FirstMethod();
if(!success)
_logger.Error(error);
// use them again but without declaration
(success, error) = SecondMethod();
if(!success)
_logger.Error(error);
Internally we decided to use the following typing conventions with tuples:
For return tuples, we use tuple variables in lowerCase
For data tuples, we use tuple variables in UpperCase
Last but not least, please remind yourself that tuples are value types!