With the addition of the Tuple class in .net 4, I have been trying to decide if using them in my design is a bad choice or not. The way I see it, a Tuple can be a shortcut
I've used tuples, both the Tuple
and the new ValueTuple
, in a number of different scenarios and arrived at the following conclusion: do not use.
Every time, I encountered the following issues:
My opinion is tuples are a detriment, not a feature, of C#.
I have somewhat similar, but a lot less harsh, criticism of Func<>
and Action<>
. Those are useful in many situations, especially the simple Action
and Func<type>
variants, but anything beyond that, I've found that creating a delegate type is more elegant, readable, maintainable, and gives you more features, like ref
/out
parameters.
Using a class like ResultType
is clearer. You can give meaningful names to the fields in the class (whereas with a tuple they would be called Item1
and Item2
). This is even more important if the types of the two fields are the same: the name clearly distinguishes between them.
Tuples are a useless framework feature in .NET 4. I think a great opportunity was missed with C# 4.0. I would have loved to have tuples with named members, so you could access the various fields of a tuple by name instead of Value1, Value2, etc...
It would have required a language (syntax) change, but it would have been very useful.
The way I see it, a Tuple is a shortcut to writing a result class (I am sure there are other uses too).
There are indeed other valuable uses for Tuple<>
- most of them involve abstracting away the semantics of a particular group of types that share a similar structure, and treating them simply as ordered set of values. In all cases, a benefit of tuples is that they avoid cluttering your namespace with data-only classes that expose properties but not methods.
Here's an example of a reasonable use for Tuple<>
:
var opponents = new Tuple<Player,Player>( playerBob, playerSam );
In the above example we want to represent a pair of opponents, a tuple is a convenient way of pairing these instances without having to create a new class. Here's another example:
var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );
A poker hand can be thought of as just a set of cards - and tuple (may be) a reasonable way of expressing that concept.
setting aside the possibility that I am missing the point of Tuples, is the example with a Tuple a bad design choice?
Returning strongly typed Tuple<>
instances as part of a public API for a public type is rarely a good idea. As you yourself recognize, tuples requires the parties involved (library author, library user) to agree ahead of time on the purpose and interpretation of the tuple types being used. It's challenging enough to create APIs that are intuitive and clear, using Tuple<>
publicly only obscures the intent and behavior of the API.
Anonymous types are also a kind of tuple - however, they are strongly typed and allow you to specify clear, informative names for the properties belonging to the type. But anonymous types are difficult to use across different methods - they were primarily added to support technologies like LINQ where projections would produce types to which we wouldn't normally want to assign names. (Yes, I know that anonymous types with the same types and named properties are consolidated by the compiler).
My rule of thumb is: if you will return it from your public interface - make it a named type.
My other rule of thumb for using tuples is: name method arguments and localc variables of type Tuple<>
as clearly as possible - make the name represent the meaning of the relationships between elements of the tuple. Think of my var opponents = ...
example.
Here's an example of a real-world case where I've used Tuple<>
to avoid declaring a data-only type for use only within my own assembly. The situation involves the fact that when using generic dictionaries containing anonymous types, it's becomes difficult to use the TryGetValue()
method to find items in the dictionary because the method requires an out
parameter which cannot be named:
public static class DictionaryExt
{
// helper method that allows compiler to provide type inference
// when attempting to locate optionally existent items in a dictionary
public static Tuple<TValue,bool> Find<TKey,TValue>(
this IDictionary<TKey,TValue> dict, TKey keyToFind )
{
TValue foundValue = default(TValue);
bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
return Tuple.Create( foundValue, wasFound );
}
}
public class Program
{
public static void Main()
{
var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
new { LastName = "Sanders", FirstName = "Bob" } };
var peopleDict = people.ToDictionary( d => d.LastName );
// ??? foundItem <= what type would you put here?
// peopleDict.TryGetValue( "Smith", out ??? );
// so instead, we use our Find() extension:
var result = peopleDict.Find( "Smith" );
if( result.First )
{
Console.WriteLine( result.Second );
}
}
}
P.S. There is another (simpler) way of getting around the issues arising from anonymous types in dictionaries, and that is to use the var
keyword to let the compiler 'infer' the type for you. Here's that version:
var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
// use foundItem...
}
IMO these "tuples" are basically all public access anonymous struct
types with unnamed members.
The only place I would use tuple is when you need to quickly blob together some data, in a very limited scope. The semantics of the data should be are obvious, so the code is not hard to read. So using a tuple (int
,int
) for (row,col) seems reasonable. But I'm hard pressed to find an advantage over a struct
with named members (so no mistakes are made and row/column aren't accidentally interchanged)
If you're sending data back to the caller, or accepting data from a caller, you really should be using a struct
with named members.
Take a simple example:
struct Color{ float r,g,b,a ; }
public void setColor( Color color )
{
}
The tuple version
public void setColor( Tuple<float,float,float,float> color )
{
// why?
}
I don't see any advantage to using tuple in the place of a struct with named members. Using unnamed members is a step backward for the readability and understandability of your code.
Tuple strikes me as a lazy way to avoid creating a struct
with actual named members. Overuse of tuple, where you really feel you/or someone else encountering your code would need named members is A Bad Thing™ if I ever saw one.
How about using Tuples in a decorate-sort-undecorate pattern? (Schwartzian Transform for the Perl people). Here's a contrived example, to be sure, but Tuples seem to be a good way to handle this kind of thing:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] files = Directory.GetFiles("C:\\Windows")
.Select(x => new Tuple<string, string>(x, FirstLine(x)))
.OrderBy(x => x.Item2)
.Select(x => x.Item1).ToArray();
}
static string FirstLine(string path)
{
using (TextReader tr = new StreamReader(
File.Open(path, FileMode.Open)))
{
return tr.ReadLine();
}
}
}
}
Now, I could have used an Object[] of two elements or in this specific example a string [] of two elements. The point being that I could have used anything as the second element in a Tuple that's used internally and is pretty easy to read.