Click here to Skip to main content
15,886,799 members
Articles / Programming Languages / C# 4.0

C# 4 - Tuples

Rate me:
Please Sign up or sign in to vote.
4.81/5 (92 votes)
9 May 2011CPOL5 min read 279.3K   104   61
Tuples can be very handy for developers, allowing them to return multiple values from a function, the creation of composite keys to Dictionaries and eliminates structs or classes just to fill combobox.

Introduction

Basically, a tuple (Tuple in C#) is an ordered sequence, immutable, fixed-size and of heterogeneous objects, i.e., each object being of a specific type.

The tuples are not new in programming. They are already used in F#, Python and databases. However, they are new to C#. The tuples were introduced in C# 4.0 with dynamic programming.

To learn more, visit: http://msdn.microsoft.com/en-us/library/system.tuple.aspx.

Background

A) "Each line that consists of an ordered list of columns is a record or tuple. The records may not contain information on all columns, and may take null values when this becomes necessary."

http://pt.wikipedia.org/wiki/Banco_de_dados_relacional#Registros_.28ou_tuples.29

Example

SQL
Insert Into Tb_clients values (1,’Frederico’, ‘1975-03-24’)

In this example, (1,’Frederico’, ‘1975-03-24’) is a tuple.

B) “A tuple, in mathematics, is a fixed-size ordered sequence of objects”

http://es.wikipedia.org/wiki/Tuple

Example: In the equation, 2x2 - 4x - 3, the sequence (2, -4, -3) is a tuple.

C) "An enupla (also known as n-tuple) is an ordered sequence of n elements, which can be defined by the ordered pair of recursion.

The main properties that distinguish an enupla are:

  • An enupla can contain an object more than once.
  • Objects are necessarily represented in the given order. "

http://pt.wikipedia.org/wiki/Tuple

Tuples in .NET 4.0

While anonymous types have similar functionality in C#, they cannot be used as return of methods, as can the type Tuple.

The KeyValuePair<TKey, TValue> can be compared to a tuple<T1, T2>, a significant difference is that KeyValuePair is a struct and a Tuple is a class.

A tuple is an ordered sequence, immutable, fixed size of heterogeneous objects.

Ordered sequence:

The order of items in a tuple follows the order used at the time of its creation.

Immutable:

All properties are read-only tuple, i.e., once created, it cannot be changed.

Fixed Size:

The size is set at the time of its creation. If it was created with three items, you cannot add new items.

Of heterogeneous objects:

Each item has a specific and independent of the type of the other item.

Disadvantages

As Tuples don’t have an explicit semantic meaning, your code becomes unreadable.

Creating Tuples

In C#, Tuple is a static class that implements the "Factory" Pattern to create instances of Tuples. We can create an instance of a Tuple using the constructor or the static method "Create".

The static method "Create" that returns an instance of type Tuple has eight overloads:

Overload

Description

Create<T1>(T1)

Create 1-tuple, or singleton.

Create<T1, T2>(T1, T2)

Create 2-tuple, or pair.

Create<T1, T2, T3>(T1, T2, T3)

Create 3-tuple, or triple.

Create<T1, T2, T3, T4>(T1, T2, T3, T4)

Create 4-tuple, or quadruple.

Create<T1, T2, T3, T4, T5>(T1, T2, T3, T4, T5)

Create 5-tuple, or quintuple.

Create<T1, T2, T3, T4, T5, T6>(T1, T2, T3, T4, T5, T6)

Create 6-tuple, or sextuple.

Create<T1, T2, T3, T4, T5, T6, T7>(T1, T2, T3, T4, T5, T6, T7)

Create 7-tuple, or septuple.

Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1, T2, T3, T4, T5, T6, T7, T8)

Create 8-tuple, or octuple.

Example

C#
Tuple<int, string, DateTime> _cliente = 
                Tuple.Create(1, "Frederico", new DateTime(1975, 3,24)); 

Tuples have a limit of 8 items. If you want to create a tuple with more items, we have to create nested Tuples.

The eighth item of the tuple has necessarily to be another Tuple. The example below will generate an exception.

C#
// Error: The eighth element must be a tuple.
var t8 = new Tuple<int,int,int,int,int,int,int,int>(1, 2, 3, 4, 5, 6, 7, 8);  

To create a tuple with 8 items, we must do the following:

C#
var t8 = new Tuple<int,int,int,int,int,int,int,Tuple<int>>
                                        (1, 2, 3, 4, 5, 6, 7, Tuple.Create(8));
var Item8 = t8.Rest.Item1; 

To create a tuple with more than 8 items, we do as follows:

C#
var t12 = new Tuple<int,int,int,int,int,int,int,Tuple<int,int,int,int, int>>
   	(1, 2, 3, 4, 5, 6, 7, new Tuple<int,int,int, int,int>(8,9,10, 11, 12));
	<>var Item10 = t12.Rest.Item3;

What Does A Tuple Represent?

Tuples do not have names that may have some significance. The attributes of a tuple are called "Item1", "Item2", and so on.

Two Tuples can be equal, but that doesn’t mean they are the same. Its meaning is not explicit, which can make your code less readable. For example, the following two tuples are equal, but represent different things:

(3, 9): Product Code 3 and Quantity 9
(3, 9): 3 and 9 are the codes of clients returned by a query.

As seen above, the fact that a tuple doesn’t carry information about its meaning, its use is generic and the developer decides what it will mean at the time of its creation and use.

So, Why Should We Use Them?

A) Return of Methods

Tuples provide a quick way to group multiple values into a single result, which can be very useful when used as a return of function, without the need to create parameters "ref" and / or "out ".

Example

C#
using System;
namespace TuplesConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var _cliente1 = RetornaCliente();
            Console.WriteLine("O código do usuário1 é: {0}", _cliente1.Item1);
            Console.WriteLine("O Nome do usuário1 é: {0}", _cliente1.Item2);
            Console.WriteLine("A data de nascimento do usuário1 é: {0}", 
                                        _cliente1.Item3.ToString("dd/MM/yyyy"));
            Console.Read();
        }
        static Tuple<int, string, DateTime> RetornaCliente()
        {
            Tuple<int, string, DateTime> _cliente = 
                        Tuple.Create(1, "Frederico", new DateTime(1975, 3, 24));
            return _cliente;
        }
    }
}

Another example of methods return is when we must return a list of an anonymous type. In this case, we can easily replace this type by tuples.

Example

C#
using System;
using System.Collections.Generic;
using System.Linq;
namespace TuplesConsoleTest
{
    class Program
    {
        static List<Tuple<int, string, string, DateTime>> lista;
        static void Main(string[] args)
        {
            CarregaLista();
            var result = SelecionaCLientes("M");
            foreach (var r in result)
            { 
                Console.WriteLine("Cliente: {0} \t Nome: {1}", r.Item1, r.Item2);
            }
            Console.Read();
        }
        private static void CarregaLista()
        {
            lista = new List<Tuple<int, string, string, DateTime>>();
            lista.Add(new Tuple<int, string, string, DateTime>
                            (0, "", "", DateTime.MinValue));
            lista.Add(new Tuple<int, string, string, DateTime>
                            (1, "Fred", "M", new DateTime(1975, 3, 24)));
            lista.Add(new Tuple<int, string, string, DateTime>
                            (2, "Rubia", "F", new DateTime(1983, 12, 17)));
            lista.Add(new Tuple<int, string, string, DateTime>
                            (3, "João", "M", new DateTime(2004, 4, 16)));
            lista.Add(new Tuple<int, string, string, DateTime>
                            (4, "Tatá", "F", new DateTime(1999, 7, 14)));
        }
        private static IEnumerable<Tuple<int, string>> SelecionaCLientes(string sex)
        {
            var ret = from t in lista
                      where t.Item3 == sex
                      select new Tuple<int, string>(t.Item1, t.Item2);
            return ret;
        }
    }
} 

B) Composite Key in a Dictionary

Due to the interface IEquatable defines GetHashCode(), the implementation of the interface IStructuralEquatable creates a Hash code combining the members Hash codes, allowing the use of tuples as a composite key for a collection of type Dictionary.

Example

C#
using System;
using System.Collections.Generic;
namespace TuplesConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var lista = ListaClienteConta();
            var chave = Tuple.Create(1, 1);
            Console.WriteLine("Saldo selecionado é: {0}", 
                        lista[chave].Saldo.ToString());

            Console.Read();
        }
        
        public static Dictionary<Tuple<int, int>, ClienteConta> ListaClienteConta()
        {
            Dictionary<Tuple<int, int>, ClienteConta> lista =
                                new Dictionary<Tuple<int, int>, ClienteConta>();
            ClienteConta cc1 = new ClienteConta(){
                Codigo_Cliente = 1,
                Codigo_Conta = 1,
                Saldo = 525.00 };
            ClienteConta cc2 = new ClienteConta(){
                Codigo_Cliente = 1,
                Codigo_Conta = 2,
                Saldo = 765.00 };
            lista.Add(Tuple.Create(cc1.Codigo_Cliente, cc1.Codigo_Conta), cc1);
            lista.Add(Tuple.Create(cc2.Codigo_Cliente, cc2.Codigo_Conta), cc2);
            return lista;
        }
    }
    public class ClienteConta
    {
        public int Codigo_Cliente { get; set; }
        public int Codigo_Conta { get; set; }
        public double Saldo { get; set; }
    }
} 

C) Replace Classes or Structs that are Created Just to Carry a Return or to Fill a List

Using the Tuple, we don’t need to create classes or structures to store only temporary values, such as creating a struct or class to add values to a combobox or listbox. With the tuples, it will no longer be necessary to create them.

Example

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace TuplesTest
{
    public partial class Form1 : Form
    {
        List<Tuple<int, string>> lista = new List<Tuple<int, string>>();
        public Form1()
        {
            InitializeComponent();
            lista.Add(Tuple.Create(7, "Tânia"));
            lista.Add(Tuple.Create(2, "Rúbia"));
            lista.Add(Tuple.Create(4, "Haroldo"));
            lista.Add(Tuple.Create(1, "Frederico"));
            lista.Add(Tuple.Create(3, "João"));
            lista.Add(Tuple.Create(5, "Carlos"));
            lista.Add(Tuple.Create(6, "Samanta"));
            lista.Add(Tuple.Create(8, "Marcio"));
            lista.Add(Tuple.Create(9, "Carla"));
            lista.Add(Tuple.Create(10, "Francisco"));
        }
        private List<Tuple<int, string>> RetornaListaPessoasOrdenadaPorNome()
        {
            var lstOrdenada = lista.OrderBy(t => t.Item2).Select(t=>t).ToList();
            return lstOrdenada;
        }
        private List<Tuple<int, string>> RetornaListaPessoasOrdenadaPorCodigo()
        {
            var lstOrdenada = lista.OrderBy(t => t.Item1).Select(t => t).ToList();
            return lstOrdenada;
        }
        private void btnOK_Click(object sender, EventArgs e)
        {
            List<Tuple<int, string>> listaOrdenada;
            if (rbtNome.Checked)
                listaOrdenada = RetornaListaPessoasOrdenadaPorNome();
            else
                listaOrdenada = RetornaListaPessoasOrdenadaPorCodigo();
            lstNomes.DataSource = listaOrdenada;
            lstNomes.ValueMember = "Item1";
            lstNomes.DisplayMember = "Item2";
        }
    }
} 

form.JPG

Comparing and Ordering

The interfaces IStructuralComparable and IStructuralEquatable were introduced in .NET 4.0 to assist in supporting Tuples.

A tuple is equal to another if and only if all items are equal, i.e., t1.Item1 ==t2.Item1 and t1.Item2 == t2.Item2, and so on.

To sort, a comparison is made on individual items, i.e., the comparison is made in the first Item1 if t1.Item1> t2.Item1 then Tuple t2 is the smallest, if t1.Item1 == t2.Item1 then the comparison is made in item2 and so on.

To use the interfaces IComparable, IEquatable, IStructuralComparable and IStructuralEquatable, we must make the cast to the desired interface explicitly.
C#
Tuple<int, int> t1 = Tuple.Create(3, 9);
Tuple<int, int> t2 = Tuple.Create(3, 9);
Tuple<int, int> t3 = Tuple.Create(9, 3);
Tuple<int, int> t4 = Tuple.Create(9, 4);
 
Console.WriteLine("t1 = t2 : {0}", t1.Equals(t2)); //true
Console.WriteLine("t1 = t2 : {0}", t1 == t2); //false
Console.WriteLine("t1 = t3 : {0}", t1.Equals(t3)); // false
 
Console.WriteLine("t1 < t3 : {0}", ((IComparable)t1).CompareTo(t3) < 0); //true
Console.WriteLine("t3 < t4 : {0}", ((IComparable)t3).CompareTo(t4) < 0); //true

Conclusion

While the indiscriminated use of Tuples affects the readability of the code, its use at the appropriate time can be very handy for developers, allowing them to return multiple values from a function without the need to create parameters "ref" and / or "out", allowing the creation of composite keys to collections of type Dictionary and eliminates the need to create structs or classes or just to fill combobox or lists.

History

  • 9th May, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader ActionPoint
Ireland Ireland
I'm Brazilian currently living in in Limerick, Ireland. I've been working with software development since 1996.

Comments and Discussions

 
GeneralMy vote of 5 Pin
JIANGWilliam1-Mar-18 15:51
JIANGWilliam1-Mar-18 15:51 
QuestionMy vote of 5 Pin
Snesh Prajapati10-Nov-13 19:51
professionalSnesh Prajapati10-Nov-13 19:51 
GeneralFriends DONT let Friends use Tuples! Pin
rittjc11-May-13 13:35
rittjc11-May-13 13:35 
GeneralRe: Friends DONT let Friends use Tuples! Pin
Yuriy Loginov27-May-13 5:50
professionalYuriy Loginov27-May-13 5:50 
GeneralRe: Friends DONT let Friends use Tuples! Pin
rittjc27-May-13 19:17
rittjc27-May-13 19:17 
GeneralRe: Friends DONT let Friends use Tuples! Pin
Yuriy Loginov28-May-13 3:47
professionalYuriy Loginov28-May-13 3:47 
GeneralRe: Friends DONT let Friends use Tuples! Pin
rittjc28-May-13 12:13
rittjc28-May-13 12:13 
Well, I think you are reaching here, reaching for a way to justify the usage of Tuples. Not that thinking outside the box is ever really a bad thing, but I believe in the case of tuples, its just a bad concept from the beginning and it brings nothing to the party.

I think the convenience you seek in then is a way of returning heterogeneous "typed" items without a formal named declaration like a structure.

Rather than me just bashing the idea let me break down what is at stake and why I believe this general approach is flawed.

If a person is targeting sound practices then I have to assume they value something like "readability". Certainly there are times when you want to do something quick and dirty in code that has no projected lifetime. That's when hacking is fine, if you are faster at hacking, then we turn the blind eye to the fact we are practicing bad habits.


But, if you are looking at code that has a future and may even be expanded, then to not use sound practices is "death by a thousand cuts". Most are too oblivious to realize how hard they make life on themselves and others, and then the rest do not care at all. To the latter, I would never give any advice. But to those that are simply not aware, readability often turns out to be as much as if not more of an issue to one's self than it is, even to others. Later you look back at the code and have no idea what you were thinking and have to waste time analyzing what you did.

If you are returning a bunch of items from a method, then in sound programming, you have to question why. It sounds like a poor design. A factory class does custom create attributes, etc but it returns an object in the form of the base class which is formally defined.

Now, you have two possible cases in returning multiple data items. They are that the items you are returning are either related or they are not. Now, first, if they are related, then there should be some object representing this relationship like a structure. It only makes sense to formally define these items so the structure is what is sought in concept rather than bits and pieces.

Now if it is the other situation where you are returning unrelated data, then that begs to question why you do not have separate "Getxxx"'s because you do not code a method up for one random case. If there is no object to pass into it (via ref myObject) then why would one method handle so much unrelated data? Again something is out of control. That suggests it is too complex a routine and makes the code inflexible. Think about code that that might want just the "float" data returned. Do they have to take all the other stuff they don't care about and add the time of loading and unloading a Tuple when all they want could be returned on the stack rather than a costly heap allocation?

Now the thing that makes the Tuple of questionable value is, the caller and the called method (GetItem) have to "coordinate" and maintain what each item represents, what order, what types and both have to use a meaningless names to synchronize these items (i.e. Item1, Item2). Both have to remember the types in their order. If are going to do that, why not just use an object[] since the members (item order) is ambiguous and both must have apriori knowledge on both sides of order and types and object[] will not use the heap if the types are all value types.

An object that calls another object should be is ignorant about what is going on and what data is used by the called method as possible. That's just OOD 101. If that is not practical for some reason then there should be a contract between them (i.e. a formally defined structure or class) that will encapsulate as much details on that data is possible.

C# certainly opens the world up for lots of people to be productive and develop code much faster than practical. Not everyone is formally trained in a college or formal training environment and many are "self taught". There are so many things you can do to make your way like going through thick mud and because people don't understand the concepts that make you efficient and those that make you inefficient and high maintenance the flexibility of C# can turn out to bury them in the unnecessary. Its bad enough that it takes a while to learn how to use object oriented concepts and coding elegance to where you learn to recognize traps and pitfalls.

To me the Tuple makes C# more susceptible to hacking, and less readable, rather than bringing more flexibility to it. I really do not think Tuples were put in because the architects saw it as a benefit. It was more like someone on the standards board used Tuples in past in a language where there were benefits to them and pushed the inclusion of them for personal reasons. Either that or someone did it just for "marketing" or "comparison" reasons to compare to some other language say like JavaScript or something like that in order to not have it used against the language.

But, if there were a benefit I could see in them then I say "not my favorite but to each his own". But, since they are really constructs of older generation languages which cannot leverage object oriented primitives like every object having a base class and run-time type info of reflection, and other goodies in modern generation languages, it just has trouble finding a place in higher infrastructure to reveal true advantages you can leverage.


Not being a language purist or bigot, but I think they were a really poor thing to add to C#. But, there are always humans driving the things that become standards so there will be more bad ideas in time. We only hope they remain "optional" rather than get in the way.

-j
QuestionNice one! Pin
Sander Rossel29-Mar-13 21:15
professionalSander Rossel29-Mar-13 21:15 
GeneralMy vote of 5 Pin
Belial0916-Jan-13 21:32
Belial0916-Jan-13 21:32 
SuggestionIterate over two or more collections Pin
daflodedeing22-Nov-12 22:31
daflodedeing22-Nov-12 22:31 
Questiontuples Pin
arup200514-Jul-12 7:02
arup200514-Jul-12 7:02 
SuggestionMy vote of 5 Pin
Akram El Assas11-May-12 10:39
Akram El Assas11-May-12 10:39 
GeneralRe: My vote of 5 Pin
fmsalmeida11-May-12 11:39
professionalfmsalmeida11-May-12 11:39 
GeneralMy vote of 5 Pin
Dave Kerr12-Feb-12 12:11
mentorDave Kerr12-Feb-12 12:11 
GeneralMy vote of 5 Pin
DaveyM695-Dec-11 8:56
professionalDaveyM695-Dec-11 8:56 
Question+5 and a question ... Pin
BillWoodruff5-Dec-11 7:00
professionalBillWoodruff5-Dec-11 7:00 
Question'unreadable' is a little harsh Pin
dave.dolan19-Jun-11 18:11
dave.dolan19-Jun-11 18:11 
AnswerRe: 'unreadable' is a little harsh Pin
fmsalmeida20-Jun-11 2:38
professionalfmsalmeida20-Jun-11 2:38 
GeneralMy vote of 5 Pin
Jonathan Cardy19-Jun-11 1:14
Jonathan Cardy19-Jun-11 1:14 
GeneralRe: My vote of 5 Pin
fmsalmeida20-Jun-11 2:37
professionalfmsalmeida20-Jun-11 2:37 
GeneralMy vote of 5 Pin
Monjurul Habib17-Jun-11 10:39
professionalMonjurul Habib17-Jun-11 10:39 
GeneralRe: My vote of 5 Pin
fmsalmeida20-Jun-11 2:36
professionalfmsalmeida20-Jun-11 2:36 
GeneralMy vote of 5 Pin
Andy Missico15-Jun-11 9:17
Andy Missico15-Jun-11 9:17 
GeneralRe: My vote of 5 Pin
fmsalmeida20-Jun-11 2:36
professionalfmsalmeida20-Jun-11 2:36 
Generalnice Pin
BillW3313-Jun-11 4:19
professionalBillW3313-Jun-11 4:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.