Tuesday, July 13, 2010

To Tuple or Not To Tuple

Yesterday I answered a question on StackOverflow about “What is the most inuitive way to ask for objects in pairs?” The question was really about using KeyValuePair<,>, but a lot of the answers suggested to use Tuple<> instead which is a new construct in .Net 4.

From the MSDN documentation we read:

“A tuple is a data structure that has a specific number and sequence of values. The Tuple<T1, T2> class represents a 2-tuple, or pair, which is a tuple that has two components. A 2-tuple is similar to a KeyValuePair<TKey, TValue> structure.”

As long as you only have two elements, it doesn’t really matter if you use Tuple<,> or KeyValuePair<,>. But keep in mind that a Tuple is a class while KeyValuePair is a struct. So for certain scenarios one would be preferable over the other. ( A Tuple can have 8 direct elements, or it can nest it self to create a n-tuple.)

So over to the real question, should you use it, or when should you use it? In my opinion it boils down to expressing the intent of your code.

Consider the two following lines of code:

List<Tuple<int, int>> list = new List<Tuple<int, int>>();
List<Point> list = new List<Point>();

They can both be used for holding an x/y coordinate, but the Point struct clearly is more expressive, letting the reader know we’re talking about coordinates.

This does not mean we should not use the Tuple class. The following is also expressive and shows intent

Tuple<Man, Woman> couple = new Tuple<Man, Woman>(m, w);

This leads me to the conclusion that it’s ok to use Tuple<> as long as the types in the Tuple are expressive, meaning they are not base types. A base type says nothing about what it holds. An int can hold a coordinate, an age or any other number of things, but if you wrap your coupled data in a pairing class you can express what you are working with.

Would you use

var bmiList = new List<Tuple<double, double>>();
var bmi = new Tuple<double, double>(180,75);
bmiList.Add(bmi);

or

class BMI
{
public double Height;
public double Weight;
}

var bmiList = new List<BMI>();
BMI bmi = new BMI {Height = 180, Weight = 75};
bmiList.Add(bmi);

A good use for Tuple’s is for methods that need to return multiple values, and the values are only used locally/once in the return. They are not passed around.

Here’s an example where a Tuple could be preferable to multiple out parameters.

public Tuple<bool, Stream, long> GetStreamAndSpaceAvail(string path)
{
if (File.Exists(path))
return new Tuple<bool, Stream, long>(true, File.OpenRead(path), new DriveInfo("c:").AvailableFreeSpace);
return new Tuple<bool, Stream, long>(false, null, 0);
}

public void usage()
{
Tuple<bool, Stream,long> result = GetStreamAndSpaceAvail("somepath");
if (result.Item1 && result.Item3 > 1000)
{
result.Item2.Write(...);
}
}

compared to

public bool GetStreamAndSpaceAvail(string path, out Stream stream, out long freeSpace)
{
freeSpace = new DriveInfo("c:").AvailableFreeSpace;
if (File.Exists(path))
{
stream = File.OpenRead(path);
return true;
}
stream = null;
return false;
}

public void usage()
{
Stream s;
long freeSpace;
if(GetStreamAndSpaceAvail("somepath", out s, out freeSpace) && freeSpace > 1000)
{
s.Write(...);
}
}

I’d love to hear others opinions on this as well.

1 comment:

  1. Here is a benchmark between Tuple and KeyValuePair. It is worth looking at : http://www.dotnetperls.com/tuple-keyvaluepair

    ReplyDelete