98

I have seen the Tuple introduced in .Net 4 but I am not able to imagine where it can be used. We can always make a Custom class or Struct.

Tim
  • 7,401
  • 13
  • 61
  • 102
Amitabh
  • 59,111
  • 42
  • 110
  • 159
  • 13
    Maybe this is a good place to note that this topic is very old. Look what happened in [C# 7](https://www.google.com/search?q=tuple+c%23+7)! – maf-soft Mar 31 '17 at 10:34

19 Answers19

83

That's the point - it is more convenient not to make a custom class or struct all the time. It is an improvement like Action or Func... you can make this types yourself, but it's convenient that they exist in the framework.

Appulus
  • 18,630
  • 11
  • 38
  • 46
tanascius
  • 53,078
  • 22
  • 114
  • 136
  • 5
    Probably worth pointing out from [MSDN](https://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx): *"Any public static members of this type are thread safe. **Any instance members are not guaranteed to be thread safe.**"* – Matt Borja Nov 17 '15 at 20:43
  • Well, maybe the language designers should have made it easier to create custom types on the fly, then? So we could keep the same syntax instead of introducing yet another one? – Thomas Eyde Mar 22 '17 at 08:10
  • @ThomasEyde which is exactly what they've been doing for the last 15 years. That's how expression bodied members were added, and now value tuples. Record-style classes narrowly missed the cut back in August (9 months before this comment) for this version of C# 7, and are probably coming out in C# 8. Also note that *value* tuples offer value equality where plain-old classes don't. Introducing all this back in 2002 would require prescience – Panagiotis Kanavos May 11 '17 at 07:12
  • what do you mean by `convenient not to make a custom class`. Do you mean creating custom class instance using `=new..()`? – user786 Apr 20 '18 at 04:11
75

With tuples you could easily implement a two-dimensional dictionary (or n-dimensional for that matter). For example, you could use such a dictionary to implement a currency exchange mapping:

var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);

decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
MarioVW
  • 2,225
  • 3
  • 22
  • 28
26

There's an excellent article in MSDN magazine that talks about the belly-aching and design considerations that went into adding Tuple to the BCL. Choosing between a value type and a reference type is particularly interesting.

As the article makes clear, the driving force behind Tuple was so many groups inside of Microsoft having a use for it, the F# team up front. Although not mentioned, I reckon that the new "dynamic" keyword in C# (and VB.NET) had something to do with it as well, tuples are very common in dynamic languages.

It is otherwise not particularly superior to creating your own poco, at least you can give the members a better name.


UPDATE: due for a big revision in C# version 7, now getting a lot more syntax love. Preliminary announcement in this blog post.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
24

Here's a small example - say you have a method that needs to lookup a user's handle and email address, given a user Id. You can always make a custom class that contains that data, or use a ref / out parameter for that data, or you can just return a Tuple and have a nice method signature without having to create a new POCO.

public static void Main(string[] args)
{
    int userId = 0;
    Tuple<string, string> userData = GetUserData(userId);
}

public static Tuple<string, string> GetUserData(int userId)
{
    return new Tuple<string, string>("Hello", "World");
}
Tejs
  • 40,736
  • 10
  • 68
  • 86
  • 10
    This is a good example however, does not justify the usage of Tuple. – Amitabh Apr 30 '10 at 15:48
  • 7
    A tuple is a decent fit here since you're returning distinct values; but a tuple shines more when you're returning multiple values of different *types*. – Mark Rushakoff Apr 30 '10 at 16:41
  • 22
    Another good example would be int.TryParse, as you could eliminate the output parameter and instead use a Tuple. So you could have `Tuple TryParse(string input)` and instead of having to use an output parameter, you get both values back in a tuple. – Tejs Apr 30 '10 at 17:37
  • 3
    in fact, that's exactly what happens when you call any TryParse method from F#. – Joel Mueller Apr 30 '10 at 17:56
23

I used a tuple to solve Problem 11 of Project Euler:

class Grid
{
    public static int[,] Cells = { { 08, 02, 22, // whole grid omitted

    public static IEnumerable<Tuple<int, int, int, int>> ToList()
    {
        // code converts grid to enumeration every possible set of 4 per rules
        // code omitted
    }
}

Now I can solve the whole problem with:

class Program
{
    static void Main(string[] args)
    {
        int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
        Console.WriteLine("Maximum product is {0}", product);
    }
}

I could have used a custom type for this, but it would have looked exactly like Tuple.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
16

C#'s tuple syntax is ridiculously bulky, so tuples are painful to declare. And it doesn't have pattern matching, so they're also painful to use.

But occasionally, you just want an ad-hoc grouping of objects without creating a class for it. For example, let's say I wanted to aggregate a list, but I wanted two values instead of one:

// sum and sum of squares at the same time
var x =
    Enumerable.Range(1, 100)
    .Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));

Instead of combining a collection of values into a single result, let's expand a single result into a collection of values. The easiest way to write this function is:

static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
    Tuple<T, State> res;
    while ((res = f(seed)) != null)
    {
        yield return res.Item1;
        seed = res.Item2;
    }
}

f converts some state into a tuple. We return the first value from the tuple and set our new state to the second value. This allows us to retain state throughout the computation.

You use it as such:

// return 0, 2, 3, 6, 8
var evens =
    Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
    .ToList();

// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
    Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
    .Take(10).ToList();

evens is fairly straightforward, but fibs is a little more clever. Its state is actually a tuple which holds fib(n-2) and fib(n-1) respectively.

Juliet
  • 80,494
  • 45
  • 196
  • 228
7

Tuples are great for doing multiple async IO operations at a time and returning all the values together. Here is the examples of doing it with and without Tuple. Tuples can actually make your code clearer!

Without (nasty nesting!):

Task.Factory.StartNew(() => data.RetrieveServerNames())
    .ContinueWith(antecedent1 =>
        {
            if (!antecedent1.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
                Task.Factory.StartNew(() => data.RetrieveLogNames())
                    .ContinueWith(antecedent2 =>
                        {
                            if (antecedent2.IsFaulted)
                            {
                                LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
                                Task.Factory.StartNew(() => data.RetrieveEntryTypes())
                                    .ContinueWith(antecedent3 =>
                                        {
                                            if (!antecedent3.IsFaulted)
                                            {
                                                EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
                                            }
                                        });
                            }
                        });
            }
        });

With Tuple

Task.Factory.StartNew(() =>
    {
        List<string> serverNames = data.RetrieveServerNames();
        List<string> logNames = data.RetrieveLogNames();
        List<string> entryTypes = data.RetrieveEntryTypes();
        return Tuple.Create(serverNames, logNames, entryTypes);
    }).ContinueWith(antecedent =>
        {
            if (!antecedent.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
                LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
                EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
            }
        });

If you were using an anonymous function with an implied type anyway then you aren't making the code less clear by using the Tuple. Retuning a Tuple from a method? Use sparingly when code clarity is key, in my humble opinion. I know functional programming in C# is hard to resist, but we have to consider all of those old clunky "object oriented" C# programmers.

AndyClaw
  • 740
  • 8
  • 15
7

I don't like the abuse of them, since they produce code that doesn't explain itself, but they're awesome to implement on-the-fly compound keys, since they implement IStructuralEquatable and IStructuralComparable (to use both for lookup and ordering purposes).

And they combine all of their items' hashcodes, internally; for example, here is Tuple's GetHashCode (taken from ILSpy):

    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
    {
        return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
    }
Notoriousxl
  • 1,540
  • 1
  • 16
  • 27
5

Tuples are heavily used in functional languages which can do more things with them, now F# is a 'official' .net language you may want to interoperate with it from C# and pass them between code written in two languages.

Mant101
  • 2,705
  • 1
  • 23
  • 27
  • Tuples are also built in types for some popular scripting languages such as Python and Ruby (that also have .Net implementations for interop... IronPython/Ruby). – MutantNinjaCodeMonkey Dec 21 '15 at 23:58
5

I tend to avoid Tuple for most scenarios since it hurts readability. However, Tuple is useful when you need to group unrelated data.

For example, suppose you have a list of cars and the cities in which they were purchased:

Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle

You want to aggregate the counts for each car per city:

Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]

To do this, you create a Dictionary. You have a few options:

  1. Create a Dictionary<string, Dictionary<string, int>>.
  2. Create a Dictionary<CarAndCity, int>.
  3. Create a Dictionary<Tuple<string, string>, int>.

Readability is lost with the first option. It will require you to write a lot more code.

The second option works and is succinct, but car and city aren't really related and probably don't belong in a class together.

The third option is succinct and clean. It's a good use of Tuple.

John Kurlak
  • 6,594
  • 7
  • 43
  • 59
4

A few examples off the top of my head:

  • An X and Y location (and Z if you like)
  • a Width and Height
  • Anything measured over time

For example you wouldn't want to include System.Drawing in a web application just to use Point/PointF and Size/SizeF.

James Westgate
  • 11,306
  • 8
  • 61
  • 68
  • 2
    `Tuple` is as handy in critical situations as `Point` or `SomeVector` may be useful when doing graphic operations. It highlights two values that are very tied to each other. I've often see properties named StartTime and EndTime when reading code, but even better than a date time and a duration, a Tuple forces you to consider both values everytime you are operating in this area of your business logic. Or returning "hey, data has changed (bool), here is the data (other type)" in heavy processed instead of going through intensive bindings. – Léon Pelletier Aug 21 '15 at 01:15
3

You should be very careful with using Tuple and probably think twice before do this. From my previous experience I found out that using Tuple makes code very difficult to read and support in the future. A while ago, I had to fix some code where tuples were used almost everywhere. Instead of thinking about proper object models, they just used tuples. That was nightmare... sometimes I wanted to kill the guy who wrote the code...

Don't want to say that you shouldn't use Tuple and it's evil or something and I'm hundred percent sure there are some tasks where the Tuple is the best candidate to be used, but probably you should think again, do you REALLY need it?

Mr. Pumpkin
  • 6,212
  • 6
  • 44
  • 60
1

The best use for Tuples I have found is when needing to return more than 1 type of object from a method, you know what object types and number they will be, and it is not a long list.

Other simple alternatives would be using an 'out' parameter

private string MyMethod(out object)

or making a Dictionary

Dictionary<objectType1, objectType2>

Using a Tuple however saves either creating the 'out' object or having to essentially look-up the entry in the dictionary;

sidjames
  • 11
  • 1
1

Just found the solution of one of my issues in Tuple. It is like declaring a class in scope of a method, but with lazy declaration of its fields names. You operate with collections of tuples, its single instances and then create a collection of anonymous type with the required field names, basing on your tuple. This avoids you from creating the new class for this purpose.

The task is to write a JSON response from LINQ without any additional classes:

 //I select some roles from my ORM my with subrequest and save results to Tuple list
 var rolesWithUsers = (from role in roles
                       select new Tuple<string, int, int>(
                         role.RoleName, 
                         role.RoleId, 
                         usersInRoles.Where(ur => ur.RoleId == role.RoleId).Count()
                      ));

 //Then I add some new element required element to this collection
 var tempResult = rolesWithUsers.ToList();
 tempResult.Add(new Tuple<string, int, int>(
                        "Empty", 
                         -1,
                         emptyRoleUsers.Count()
                      ));

 //And create a new anonimous class collection, based on my Tuple list
 tempResult.Select(item => new
            {
                GroupName = item.Item1,
                GroupId = item.Item2,
                Count = item.Item3
            });


 //And return it in JSON
 return new JavaScriptSerializer().Serialize(rolesWithUsers);

Of cause we could do this with declaring a new Class for my groups, but the idea to create such an anonimous collections without declaring of new classes.

EvilDr
  • 8,943
  • 14
  • 73
  • 133
Alex
  • 8,827
  • 3
  • 42
  • 58
1

Well in my case, I had to use a Tuple when I found out that we cannot use out parameter in an asynchronous method. Read about it here. I also needed a different return type. So I used a Tuple instead as my return type and marked the method as async.

Sample code below.

...
...
// calling code.
var userDetails = await GetUserDetails(userId);
Console.WriteLine("Username : {0}", userDetails.Item1);
Console.WriteLine("User Region Id : {0}", userDetails.Item2);
...
...

private async Tuple<string,int> GetUserDetails(int userId)
{
    return new Tuple<string,int>("Amogh",105);
    // Note that I can also use the existing helper method (Tuple.Create).
}

Read more about Tuple here. Hope this helps.

Amogh Natu
  • 781
  • 1
  • 10
  • 37
  • I assume you didn't want to return an anonymous type or Array (or dynamic contents) – Andy Nov 09 '17 at 23:31
0

Changing shapes of objects when you need to send them across wire or pass to different layer of application and multiple objects get merged into one:

Example:

var customerDetails = new Tuple<Customer, List<Address>>(mainCustomer, new List<Address> {mainCustomerAddress}).ToCustomerDetails();

ExtensionMethod:

public static CustomerDetails ToCustomerDetails(this Tuple<Website.Customer, List<Website.Address>> customerAndAddress)
    {
        var mainAddress = customerAndAddress.Item2 != null ? customerAndAddress.Item2.SingleOrDefault(o => o.Type == "Main") : null;
        var customerDetails = new CustomerDetails
        {
            FirstName = customerAndAddress.Item1.Name,
            LastName = customerAndAddress.Item1.Surname,
            Title = customerAndAddress.Item1.Title,
            Dob = customerAndAddress.Item1.Dob,
            EmailAddress = customerAndAddress.Item1.Email,
            Gender = customerAndAddress.Item1.Gender,
            PrimaryPhoneNo = string.Format("{0}", customerAndAddress.Item1.Phone)
        };

        if (mainAddress != null)
        {
            customerDetails.AddressLine1 =
                !string.IsNullOrWhiteSpace(mainAddress.HouseName)
                    ? mainAddress.HouseName
                    : mainAddress.HouseNumber;
            customerDetails.AddressLine2 =
                !string.IsNullOrWhiteSpace(mainAddress.Street)
                    ? mainAddress.Street
                    : null;
            customerDetails.AddressLine3 =
                !string.IsNullOrWhiteSpace(mainAddress.Town) ? mainAddress.Town : null;
            customerDetails.AddressLine4 =
                !string.IsNullOrWhiteSpace(mainAddress.County)
                    ? mainAddress.County
                    : null;
            customerDetails.PostCode = mainAddress.PostCode;
        }
...
        return customerDetails;
    }
Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
0

An out parameter is great when there are only a few values that need to be returned, but when you start encountering 4, 5, 6, or more values that need to be returned, it can get unwieldy. Another option for returning multiple values is to create and return a user-defined class/structure or to use a Tuple to package up all the values that need to be returned by a method.

The first option, using a class/structure to return the values, is straightforward. Just create the type (in this example it is a structure) like so:

public struct Dimensions
{
public int Height;
public int Width;
public int Depth;
}

The second option, using a Tuple, is an even more elegant solution than using a userdefined object. A Tuple can be created to hold any number of values of varying types. In addition, the data you store in the Tuple is immutable; once you add the data to the Tuple through the constructor or the static Create method, that data cannot be changed. Tuples can accept up to and including eight separate values. If you need to return more than eight values, you will need to use the special Tuple class: Tuple Class When creating a Tuple with more than eight values, you cannot use the static Create method—you must instead use the constructor of the class. This is how you would create a Tuple of 10 integer values:

var values = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>> (
1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int> (8, 9, 10));

Of course, you can continue to add more Tuples to the end of each embedded Tuple, creating any size Tuple that you need.

Yashwanth Kata
  • 817
  • 8
  • 21
0

Only for prototyping - Tuples are meaningless. It convenient to use them but it's a shortcut only! For prototypes - fine. Just be sure to delete this code later.

It easy to write, hard to read. It has no visible advantages over classes, inner classes , anonymous classes etc.

bunny1985
  • 762
  • 6
  • 21
0

Well I tried 3 ways to solve the same problem in C#7 and I have found a use case for Tuples.

Working with dynamic data in web projects can sometimes be a pain when mapping etc.

I like the way the Tuple just auto mapped onto item1, item2, itemN which seems more robust to me than using array indexes where you might get caught on an out of index item or using the anonymous type where you may misspell a property name.

It feels like a DTO has been created for free just by using a Tuple and I can access all the properties using itemN which feels more like static typing without having to create a separate DTO for that purpose.

using System;

namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var tuple = GetTuple();
            Console.WriteLine(tuple.Item1);
            Console.WriteLine(tuple.Item2);
            Console.WriteLine(tuple.Item3);
            Console.WriteLine(tuple);

            Console.WriteLine("---");

            var dyn = GetDynamic();
            Console.WriteLine(dyn.First);
            Console.WriteLine(dyn.Last);
            Console.WriteLine(dyn.Age);
            Console.WriteLine(dyn);

            Console.WriteLine("---");

            var arr = GetArray();
            Console.WriteLine(arr[0]);
            Console.WriteLine(arr[1]);
            Console.WriteLine(arr[2]);
            Console.WriteLine(arr);

            Console.Read();

            (string, string, int) GetTuple()
            {
                return ("John", "Connor", 1);
            }

            dynamic GetDynamic()
            {
                return new { First = "John", Last = "Connor", Age = 1 };
            }

            dynamic[] GetArray()
            {
                return new dynamic[] { "John", "Connor", 1 };
            }
        }
    }
}
Andy
  • 2,124
  • 1
  • 26
  • 29