.NET Tips and Tricks

Blog archive

Return Multiple Values from Methods with Tuples

You have a method that returns multiple values. So you have to define a class, instantiate it, set the properties to the values, and return the resulting object, right? Well, no you don't -- at least in .NET Framework 4, which gives you a simpler way: Tuples.

A Tuple can hold up to eight values (including classes) and can be created on the fly. To declare and instantiate a Tuple that could hold two values (one string and one integer), you'd use code like this:

  Dim tup As New Tuple(Of Integer, String)

When you instantiate a new tuple, you also pass the values that will be put in the Tuple. So instantiating the Tuple and putting the number 2 and the string "fred" in it would require this code:

  Dim tup As New Tuple(Of Integer, String)
              (2, "fred")

There's no need to define a special purpose class to return some arbitrary collection of values.

Here's a more useful example: A method that returns a List of Customer objects. However, the method also returns an error code and a human-readable error message. Rather than defining a class, the code just declares a Tuple with an integer, a string, and List of Customers:

  Function ReturnData(Region As String) _
As Tuple(Of Integer, String, List(Of Customer))

Dim cust As Customer Dim lst As New List(Of Customer) Dim errorCode As Integer Dim errorMessage As String

'code to add customers to lst and catch errors

Dim tup As New Tuple(Of Integer, String, List(Of Customer)) (errorCode, errorMessage, lst) Return tup

End Function

The code that calls this method would use Item* properties on the Tuple to access the values. A Tuple, like this one, with three items stored in it will have properties called Item1, Item2, and Item3 (and all the properties will have the right data type).

To call the method, check for an error, and either display the error message or process the Customers in the list if everything works right, you'd write code like this:

  Dim retData As Tuple(Of Integer, String, List(Of Customer))
  
  retData = ReturnData("NA")
  
 If retData.Item2 < 0 Then
     MessageBox.Show(retData.Item2)
  Else
     For Each cust In retData.Item3
       'process Customers
     Next        
  End If

Editor's Note: This article has been updated to reflect that Tuples can hold up to eight values, not four.

Posted by Peter Vogel on 12/08/2011 at 1:16 PM


comments powered by Disqus

Reader Comments:

Tue, Dec 13, 2011 Peter Vogel Canada

Steve: I do agree with you that using tuples to return a value to an external client isn't a great solution--the return type from a public method forms part of the class' contract with the client and I'm certainly willing to spend the time to nail down all the parts of my public interfaces. However, for internal methods returning multiple values, I'm less interested in implementing a special class. Your suggestion of implementing a generic error class with, say, three properties (integer error code, string error message, generic (of type) offending class) is an excellent one. However, I will point out that if I ever need to return two offending objects I'm going to need to create another class. I'm wondering how much effort I'm willing to invest in engineering a comprehensive solution..

Tue, Dec 13, 2011 Steve Johnstone

Sorry, ErrorWrapper should have been a generic class like ErrorWrapper'TResult but apparently anything with html like syntax gets removed from the comment system.

Tue, Dec 13, 2011 Steve Johnstone

If you couldn't see the internal workings of the function, how would you know what item1 was in the return value? A better way would be to create a ErrorWrapper class that you could use to attach error codes and messages to any result type that you required, and then use that everywhere. I agree that the generic generic classes (is that a good way to describe them?) are useful, but the example you give isn't really a good use.

Mon, Dec 12, 2011 Peter Vogel Canada

C# Programmer: You can always recognize good programmers because they consider the maintenance and clarity of code issues first. My first thought would be that changing the output of a function--i.e. altering the public interface of a method--is always going to be an issue (and something to avoid doing). Still it does happen and it would be naive to ignore the issue. However, I think that tuples don't create as many problems as you might think. First, most changes involve adding new data rather than removing old data. Like adding columns to a table, existing code will simply ignore the additional data. More critically, most positions in the tuple (at least in my experience) are occupied by classes--I'm more likely to change the class' definition than the data type of a position in the tuple. Ever since the issue was raised about the obscurity of the ItemN syntax, I've been wondering why that doesn't bother me more. I think the reason is that I've found that the data type and variable names in the code that extracts the values from the ItemN properties have been giving me the documentation that I need--at any rate, it hasn't bugged me (yet). Finally, when making a change in the return type, I suspect that I'll track down the problem through the method calls (rather than the return type of the method) but I'll have to pay attention to my practice to be sure that's what I do. Right now, I can't think of a case where a change in a return method's data type wouldn't be just as much (or as little) a problem as the equivalent change in a tuple--but that may be failure of imagination on my part. Coupled with my dislike of cluttering my object model with a public class whose only purpose is to transfer data for a brief moment between when one particular method finishes and the data arrives at its destination...I'm comfortable with tuples. Tuples are very definitely a niche solution but it's a surprisingly common niche (well, surprising to me).

Sun, Dec 11, 2011 C# programmer

First let me say, I'm not a VB programmer. My language of choice is C# so forgive me if I'm missing something here. Using Tuples as a return type to my way of thinking is dangerous. What happens if you had a function that returns a Tuple and that function is used a few thousand times in your really big application. Now the function changes and the Tuple has more or less data in it. It seems you have to track down every place that it's used and change the declaration in the caller to match the new Tuple declaration. That coupled with the anonymous "itemN" syntax for accessing the data I don't really see the value. I would suggest not being lazy and simply create a class. Visual Studio makes it VERY easy. I've found that being lazy usually comes back to bite you in the ass.

Fri, Dec 9, 2011 Peter Vogel Canada

Kirk: Good discussion of the costs and benefits of using Object arrays. I've avoided them because of the boxing/unboxing costs you refer to but I suspect that I've exaggerated the costs in my mind. Rob: I don't about the XAML--I'll try and do a followup post. Brian: You're right about readability (and maintainability) being reduced by the Item1 method names--this getting close to "write only code" and "job security programming". You might want to take a look at using a dynamic object that lets you add (and read) properties on the fly. I'll try and do a followup post on that, also. But you really do want to avoid using ByRef parameter values--it makes it difficult (especially in Visual Basic) to determine what variables in your calling program are being updated by the method call. You have no choice with events (you have to update the properties on the e parameter to return results) but this gives you an option with ordinary methods.

Thu, Dec 8, 2011 Kirk Davis Bangkok, Thailand

While Tuples look interesting and useful (and I'll probably use them), there always *has* been a simple way to return multiple values of different types without creating a new class, instantiating, setting properties, etc, and that's the good old fashioned object array. Yes, boxing/unboxing, but its still fast and easy: Function ReturnData As Object() Dim myStuff() = New Object() {123, "abc", myClassInstance} Return myStuff End Function Then, if myData = ReturnData(), you can pull out your values with: Dim myInt As Integer = myData(0) Dim myStr As String = myData(1) Dim myCls As Whatever = myData(2) Granted, if the number of items being passed varies, you need to check myData.Length before pulling stuff out, and of course, you need to cast values to their proper types (you can check the type with .GetType though). I've used this method a lot for passing multiple arguments to background workers and to asynchronous callbacks (eg, as the UserState). I'll definitely use Tuples instead for a lot of these cases, though, so thanks for pointing them out.

Thu, Dec 8, 2011 PaleSash

Thanks for this ! I have been taking the cheap way out and declaring the values I want returned as parameters to my methods. I did not know about tuples until I read this article. Guess I now have some re-work to do !

Thu, Dec 8, 2011

If this is representative of the articles in this magazine then there is no good reason to read it. I know less about tuples than when I started to read the article.

Thu, Dec 8, 2011 Brian MacKay

Good article! It's interesting that this exists, but to me dealing with names like .Item1 and .Item2 hurts readability (and therefore maintainability, which should be what we're all striving for in most cases) too much.

Thu, Dec 8, 2011 Matt Alexandria, Va

I'm thinking in that last block you meant to check .Item1 0, since that's the numeric code. This does sound like a nice way to return a little extra from a method call.

Thu, Dec 8, 2011 Rob Perkins

Can you bind to a Tuple in XAML, for example, expose a Tuple or a collection of them in a ViewModel class? What would that look like, if you can?

Thu, Dec 8, 2011 Peter Vogel Canada

Jonathan: You're right! My fingers fumbled and I typed in 4 instead of 8 (some multiple of 2, I guess). You're also right about the daisy chaining feature (putting a tuple in one of the positions of a tuple, typically the last position) is certainly possible but not something that I'd recommend. One of the things that I like about a tuple is how easy it is to pull data out of it--trying to get the third item in the tuple in the last position of another tuple strikes me as no fun. Besides, if your method is returning more than 8 things then your method is probably doing too much.

Thu, Dec 8, 2011 Jonathan Allen

Tuples can hold up to 8 items natively, not 4. And you can daisy-chain them together for an unlimited number of items.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.