Do you have problems with C# Tuple class because items are read only?
While I was working on a small Windows Form tool in C#, which should help me to save and load parameters for a command line application, I run into the problem that not the complete textbox controls should be saved, but only the name and the text content. So, I had the requirement to save only a subset of the form fields. After thinking a while, I came to the idea to use Linq
in combination with the Tuple
class and I did some research about Tuples at all.
In mathematics, a finite sequence of elements is called a Tuple and they rise popularity with the implementation of functional programming languages. Like in mathematics, in functional programming a certain variable has a single value at all the time. So, Tuples are immutable or in other words “read only” by design and implementations like in C# are following this design.
Beside other usage of Tuples in programming languages, it’s commonly used to return a subset of data. Think about following use case: i.e. there is a list of territory definitions with fields like ZIP Code, City, Region, Population etc. and this list must be filtered and only a subset of the territory fields should be returned.
Here a code snippet for the above mentioned class definition:
// Define a class to store data class Territory { public int zip; public string city; public string region; };
With Linq
we could pretty easy do the filtering requirement, but we would have difficulties to return only a subset of the fields. Here a sample how it could be done:
// Define a sample list of objects to work with Territory[] myTerritories = new Territory[] { new Territory{zip=4153,city="Reinach",region="BL"}, new Territory{zip=8304,city="Wallisellen",region="ZH"}, new Territory{zip=3018,city="Bern",region="BE"}}; // Select a complete Territory subset variant 1 var subset1 = from Territory t in myTerritories where t.region.StartsWith("B") select t; // Select a complete Territory subset variant 2 var subset2 = myTerritories.Where(t => t.region.StartsWith("B"));
So, here comes the usage of Tuples. To return only a subset of the territory fields, this fields must be stored in new instances of the Tuple class. The result will be a list of Tuples instead of a list of Territories.
Here you find a code snippet, returning a subset of the territory structure in a list of Tuples after the filtering:
// Select only a subset of territory fields as a list of Tuples var subset3 = myTerritories.Where(t => t.region.StartsWith("B")) .Select(t => new Tuple<int, string>(t.zip, t.city));
The problem is now, that Tuples are immutable / read only and follwing code would not compile:
subset3.ElementAt(0).Item2 = "Reinach BL";
So, the best solution is, to define a new class simillar to the Tuple class with a propper constructor and use this class instead of the Tuple class. Here the complete sample code:
class Program { // Define a class to store data class Territory { public int zip; public string city; public string region; }; static void Main(string[] args) { // Define a sample list of objects to work with Territory[] myTerritories = new Territory[] { new Territory{zip=4153,city="Reinach",region="BL"}, new Territory{zip=8304,city="Wallisellen",region="ZH"}, new Territory{zip=3018,city="Bern",region="BE"}}; // Select a complete Territory subset variant 1 var subset1 = from Territory t in myTerritories where t.region.StartsWith("B") select t; // Select a complete Territory subset variant 2 var subset2 = myTerritories.Where(t => t.region.StartsWith("B")); // Select only a subset of territory fields as a list of Tuples var subset3 = myTerritories.Where(t => t.region.StartsWith("B")) .Select(t => new Tuple<int, string>(t.zip, t.city)); // because tuples are immutable/read only, these // will not compile and therefore it's commented out: /* subset3.ElementAt(0).Item2 = "Reinach BL"; */ // Select only a subset of territory fields as a list of entities var subset4 = myTerritories.Where(t => t.region.StartsWith("B")) .Select(t => new Entity<int, string>(t.zip, t.city)); // because Entity is mutable these will work: subset4.ElementAt(0).Item2 = "Reinach BL"; } } public class Entity<T1, T2> { public Entity(T1 t1, T2 t2) { Item1 = t1; Item2 = t2; } public T1 Item1 { get; set; } public T2 Item2 { get; set; } } public class Entity<T1, T2, T3> { public Entity(T1 t1, T2 t2, T3 t3) { Item1 = t1; Item2 = t2; Item3 = t3; } public T1 Item1 { get; set; } public T2 Item2 { get; set; } public T3 Item3 { get; set; } }
So, I hope you had fun and please leave a comment.
Original Post: https://www.redtoo.com/ch/blog/do-you-have-problems-with-c-tuple-class-because-items-are-read-only/