Code Smell:System.Tuple

System.Tuple is a set of generic utility classes that lets a lazy developer get away without creating a separate class. At first sight it might look handy, but it isn’t. It’s a code smell. And Tuples behave the same as anything that smells: It gets worse if you leave it and let them spread throughout the code base.

Typical use of tuples include allowing multiple return values for functions or storing temporary data inside a method.

/// <summary>
/// Recommended pressure of front and back tires of the car.
/// </summary>
public Tuple<double, double> TirePressure
{ get { return Tuple.Create(2.0, 2.4); } }
 
/// <summary>
/// Calculate the average of numbers in a sequence of sequences.
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public int AggregatedAverage(IEnumerable<IEnumerable<int>> values)
{
    var firstLevel = values.Select(s => Tuple.Create(s.Average(), s.Count()));
 
    return firstLevel.Select(fl => fl.Item1 * fl.Item2).Sum() / firstLevel.Sum(fl => fl.Item2);
}

Neither of these make the code very readable, so what should be done instead?

Use Real Types for Return Types

A method’s return type is a core piece of the signature of the function. It is as important that it is understandable as it is that the method name is understandable. We’ve left the age of variables named a, b and c so why should we accept a return type containing members Item1 and Item2?

Creating a real class or struct to hold the return values is the only reasonable thing to do in this case.

public struct TirePressureInfo
{
  readonly double front, back;
 
  public TirePressureInfo(double front, double back)
  {
    this.front = front;
    this.back = back;
  }
  public double PressureFront { get { return front; } }
  public double PressuerBack { get { return back; } }
}
 
public TirePressureInfo TirePressure
{ 
  get 
  { 
    return new TirePressureInfo(2.0, 2.4);
  } 
}

It’s substantially more code to write, but for a caller of the method it is now clear what the method returns without consulting documentation. Even if it is your own function, it’s far easier to mix up Item1 with Item2 than PressureFront and PressureBack. Creating a separate class is not the only option however. Consider a method that first returned the distance driven since the last refuel. Due to changed requirements it now returns the last three refuels.

/// <summary>
/// Distance since last three refuels.
/// </summary>
public Tuple<int, int, int> KmSinceRefueling

In this case the right thing to do is not to create a separate type, but rather to recognize it for what it is: a sequence of values.

public IEnumerable<int> KmSinceRefueling

Intermediate Values

For intermediate values inside functions the case is even more simple than for return values: use an anonymous type.

public static double AggregatedAverage(IEnumerable<IEnumerable<int>> values)
{
  var firstLevel = values.Select(s => new
  {
    Average = s.Average(),
    Count = s.Count()
  });
 
  return firstLevel.Select(fl => fl.Average * fl.Count).Sum() / firstLevel.Sum(fl => fl.Count);
}

Totally Meaningless: Tuple<T1>

If I’ve been ranting about the smell of Tuple in general so far, there is a case that has an especially stinking smell: Tuple<T1>. What’s the point of it? It just contains one public property Item1. Why not use the item directly instead?

Tuple’s Advantages

Laziness is an argument for using it. Not a good argument, but it is an argument.

Fellow developer having used Tuple extensively.

Okay, I’ll give it in, there is an advantage of Tuple classes. Not that they are better for my lazy fellow dev, but a real advantage that a custom quickly hacked together class will lack: Comparison. The Tuple classes implement IStructuralEquatable, IStructuralComparable and IComparable. Those are really basic things, but surprisingly easy to get wrong. And they increase the boiler plate coded needed for small data carrying class if it is to be used as a key to a Dictionary.

Posted in C# on 2014-08-28
  • Ognyan Dimitrov on 2014-09-26

    I have a situation when I use Tuple and it does not look so bad. The example is with mapping two exemplar types : Employee and EmployeeHistroy into one EmployeeDto which is needed for the front end. I make a Tuple and create a map for AutoMapper Tuple => Employee. Wouldn`t it be overkill to make another type just to make this map? I wonder if there is a better and cleaner approach.

    • Anders Abel on 2014-09-26

      As long as you keep the usage of Tuple internal it should be fine, it’s when you start leaking it to the outside world it gets important with meaningful names. In your case I think that the most important thing is to keep an eye on it and make sure that it remains isolated in one place. Once it starts to get used as part of visible APIs (even if only internally) I think that it should be replaced with a specific type.

  • Dave Amour on 2020-05-06

    Yes! Could not agree more, death to Tuples!

  • Leave a Reply

    Your name as it will be displayed on the posted comment.
    Your e-mail address will not be published. It is only used if I want to get in touch during comment moderation.
    Your name will be a link to this address.
Software Development is a Job – Coding is a Passion

I'm Anders Abel, an independent systems architect and developer in Stockholm, Sweden.

profile for Anders Abel at Stack Overflow, Q&A for professional and enthusiast programmers

Code for most posts is available on my GitHub account.

Popular Posts

Archives

Series

Powered by WordPress with the Passion for Coding theme.