Don't be lazy. Avoid the type "Tuple"

*Updated* 26. September 2010: Updated with comments from Adam Cogan
*Updated* 27. September 2010: Updated the comparison between anonymous types and Tuple's from blog comments
*Updated* 6. July 2017: Microsoft released C#7 that has a tuple type and tuple literal which resolves my below issues. https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/ 
Attention: From a Clean Code perspective I would consider the Primitive Obsession smell with tuples.

Tuple is a new type introduced in .NET4. My first impression of it was great, because it's a nice and easy way to group elements of a different type together. Sweet!
My first experience using the Tuple type was using it as a return value from a few methods, where I previously had an "out" parameter before, like this…
            string errormsg;
            User user;
            bool success = AuthenticationService.GetUser(usercode, out user, out errormsg);
Figure: Bad example - We should avoid "out" parameters, because it means we return 2 objects from a method.

A method should return only 1 thing, otherwise it is considered as a code smell. More on Stackoverflow here and here
Refactoring: Take #1.
I refactored the above code to this…
  
            Tuple<bool, User, string> result = AuthenticationService.GetUser(usercode);
            
            Console.WriteLine("Result {0}  with user {1}", result.Item1, result.Item2);
Figure: Better example - Here the Tuple type replaces the "out" parameters

image  But I have a problem with the readability of the code, specifically the stuff in yellow
  
            Tuple<bool, User, string> result = AuthenticationService.GetUser(usercode);
            
            Console.WriteLine("Result {0}  with user {1}", result.Item1, result.Item2);
Figure: What is Item1 and Item2?

Refactoring: Take #2.
Take 2 minutes (if you are using Resharper) and create a nice class that represents the above code better
  
    GetUserResult result = AuthenticationService.GetUser(usercode);
    if (result.Result == false)
    {
        Console.WriteLine("Result {0} Error {1} ", result.Result, result.ErrorMessage);
    }
            
    public class GetUserResult
    {
        public string ErrorMessage { get; set; }
        public bool Result { get; set; }
        public User User { get; set; }
    }
Figure: Good example - Item1 and Item2 are replaced with your own class

PS: Still not convinced? Search for "Tuple c# Item1 Item2 Item3" and you will see a lot of unreadable code samples using "Tuple"

PPS: Some have asked me: What is the difference between a "Tuple" and an "Anonymous Type"?
I use both, while keeping in my mind:
  1. Anonymous types cannot cross method boundaries  (Yes they can, if you declare the parameter as object, or by using Generics, THANKS Anonymous)
  2. Anonymous types cannot be used in the declaration of method parameters, obviously ;-)
  3. With Anonymous types you can name the properties, which is nice!
            var mySearchResult = new
                                     {
                                         Result = true,
                                         ErrorMessage = "None",
                                         User = new User("Peter", "Gfader")
                                     };
Figure: Good example - You can have nice names for properties when using anonymous type

Suggestion to Microsoft
I would like the C#/CLR team to give me the ability to give a Tuple nice property names, like this
 Tuple<bool Result, User UserFromWS, string ErrorMessage> result = AuthenticationService.GetUser(usercode);
Figure: Id like Microsoft to make this change in the C# language

22 comments:

Anonymous said...

consider the respective F# usage:

let u = (0, "hello world")

let u1 = fst u
let u2 = snd u

Peter Gfader said...

Hi zproxy

Yeah F# is great for working with tuples.
What if the tuple has more than 2 values?

Shahzad said...

hi peter,

Your refactoring code with class works, but it not logically an entity as class in OOPs terms (not having method, inheritance, abstraction)
So I think Microsoft is trying to avoid confusion/mixing concept with OOPs philosophy. So they introduced just grouping of related types with tuple.

Secondly, your point regarding readability of code with tuples is very much right. Microsoft should have introduced some method of named properties in tuple.

Have a nice day.
Shahzad Sarwar

Anonymous said...

I agree that named properties are more maintainable and hence "better" than tuples. Tuples are more useful in situations where a name would be picked arbitrarily anyway, or where you know you have something like a tuple but no good way to name it. They can also be useful if you have an API where the data is always being retrieved immediately and the method name already makes clear the nature of the data.

In other words, while tuples could be misused, it's important to not throw the baby out with the bathwater.

I also want to point out some problems with your description of anonymous types:

• Anonymous types certainly can cross method boundaries. Like anonymous methods, they aren't really anonymous. It's just that the name isn't available at compile-time. You can pass instances of anonymous types as objects and you can manipulate them with reflection.

• Not only can anonymous types be used as method parameters, that is in fact one of the most common ways they are used. They were introduced to the language to support LINQ and similar idioms, where anonymous types are commonly created and passed to generic methods, taking advantage of type inference to avoid having to provide an actual name for the type.

It's true that anonymous types are more limited in some ways than named types. But they are not nearly as limited as your blog article seems to claim.

Richard Minerich said...

F# works like this:

let result, userFromWS, errorMessage = AuthenticationService.GetUser(usercode);

In this way the tuple is automatically decomposed into three variables.

Peter Gfader said...

>> Anonymous types certainly can cross method boundaries

You are right.
But Reflection and the usage of the object type as method parameter is not very readable code though.
I was more referring to method declaration


>> Anonymous types used as method parameters, that is in fact one of the most common ways they are used.

Agree. In conjunction with generic methods they are great.


>> It's true that anonymous types are more limited in some ways than named types. But they are not nearly as limited as your blog article seems to claim.

I just wanted to highlight the differences between anon types and Tuple's

Peter Gfader said...

Thx Anonymous
I updated the post from your feedback

THANKS!

Daniel said...

Hi, I completely agree with the main point of your post - that tuples cause important information to be lost about the meaning of each separate value.

And like you showed, when using an unnamed tuple as a return value, parsing the mapping between an item's index in the tuple and its name is the CALLER's responsibility. That's just bad design.

However, the best signature for that GetUser method, is the first one you showed - using "out" parameters.

See, if a method returns multiple values, such as "user" and "errormsg", there are 2 cases to consider:
1. "user" and "errormsg" are related values, and the pair of them is a meaningful domain level entity. If that's the case, then great, you've discovered a missing class in the application's design and can refactor into a new class, that will be useful in other places as well.

2. "user" and "errormsg" are unrelated. That's OK- sometimes a method will want to return 2 completely unrelated values. If that's the case - no need to fight it, just return 2 separate values.

"GetUser" seems to fall into the second case. "user" and "error message" are unrelated concepts. Another indicator is the name of the new class- "GetUserResult". That's not a useful class, it's a workaround for returning multiple values.

Now, in C# we don't need workarounds for that- there's a great feature called "out parameters". You wrote that you avoid it because it means returning 2 objects from your method... which is exactly what you want to do, so you might as well have the syntax admit it.

Another option might be to split the method into several methods each returning a single value, sometimes that's a great solution and sometimes it's not appropriate.

Sudipta Mukherjee said...

Please check my named tuple implementation at
http://sudipta.posterous.com/generic-named-tuple-implementation-in-c-40

Peter Gfader said...

Hi Sudipta

I left a comment on your blog
http://sudipta.posterous.com/generic-named-tuple-implementation-in-c-40

CU
.peter.gfader.
http://blog.gfader.com

Peter Gfader said...

Hi Daniel

I agree with you about the signature of the GetUser method. It was the first example that I found in my current project, so I took it.

>> 2. "user" and "errormsg" are unrelated
Yes this is the case.

Thanks for the nice explanation

.peter.gfader.

BlackWasp said...

On the anonymous types and reflection point, there is a third option in C#4.0. If you use the dynamic type for the parameter you can access the properties without directly using reflection, as in:

static void Main(string[] args)
{
var anon = new { Message = "Hello, World!" };
ShowAnon(anon);
}

static void ShowAnon(dynamic anon)
{
Console.WriteLine(anon.Message);
}

I wouldn't necessarily recommend it though.

Peter Gfader said...

You are right!

I am not a big fan of dynamic... yet... maybe...
we'll see...

Good catch!

BlackWasp said...

I agree. I've not found a great use for it in a project yet but I don't do a great deal with dynamic languages currently either. Maybe my language to learn for 2011 will be a dynamic one.

Anonymous said...

I love Tuples. The should have added them in .Net 2.0 when they created generics.

How else could you write code like this:
Dictionary, string, MyEnumType>>, object> ?

Corneliu.
PS>> My background is C++ ;)

Peter Gfader said...

Hi Corneliu

Some characters got lost...
Can you paste that sample again?

.peter.gfader.

Anonymous said...

Hi Peter Gfader,

It was a nice post,probably classes would be more useful than tuples


Regards,
Kamal

Unknown said...

This is not the reason why the tuple type exists. It exists to make certain set based tasks easier to implement For example, you do not need to override .GetHashCode() and .Equals on tuples to compare them Tuple.Create(2,3)==Tuple.Create(2,3) return true.

shivprasad koirala said...

How about structres

Anonymous said...

Tuples are great for implementing dictionaries with multi-valued keys.

Adam Mendoza said...

What about this example?

public class Velocity : Tuple
{
public Velocity(double Speed, double Direction, string Units) : base(Speed, Direction, Units) { }
public double Speed { get { return this.Item1; } }
public double Direction { get { return this.Item2; } }
public string Units { get { return this.Item3; } }
}


Now instead of this garbage:

Tuple myVelocity = new Tuple(10, 2.34, "cm/s");
System.Diagnostics.Debug.Print("Speed: " + myVelocity.Item1);
System.Diagnostics.Debug.Print("Direction: " + myVelocity.Item2);
System.Diagnostics.Debug.Print("Units: " + myVelocity.Item3);
You get to do this:

Velocity myVelocity2 = new Velocity(10, 2.34, "cm/s");
System.Diagnostics.Debug.Print("Speed: " + myVelocity2.Speed);
System.Diagnostics.Debug.Print("Direction: " + myVelocity2.Direction);
System.Diagnostics.Debug.Print("Units: " + myVelocity2.Units);

Peter Gfader said...

Hi Adam

Nice example.
- Is the inheritance from Tuple really necessary?
RE: Prefer composition over inheritance
- Why not creating a inner field from type "Tuple"?
- What are some dis/advantages of that?

Post a Comment

Latest Posts

Popular Posts