110

Consider a function which returns two values. We can write:

// Using out:
string MyFunction(string input, out int count)

// Using Tuple class:
Tuple<string, int> MyFunction(string input)

// Using struct:
MyStruct MyFunction(string input)

Which one is best practice and why?

Xaqron
  • 29,931
  • 42
  • 140
  • 205
  • String is not a value type. I think you meant to say "consider a function which returns two values". – Eric Lippert Jun 17 '11 at 06:04
  • @Eric: You right. I meant immutable types. – Xaqron Jun 17 '11 at 06:07
  • and what's wrong with a class? – Lukasz Madon Jun 17 '11 at 06:22
  • 1
    @lukas: Nothing, but surely it is not in best practices. This is a lightweight value (< 16 KB) and if I gonna adding a custom code, I'll go with `struct` as `Eric` mentioned. – Xaqron Jun 17 '11 at 06:28
  • Its a little more wordy but in your case i think a KeyValuePair would be a better choice than Tuple – MikeT Oct 30 '13 at 15:41
  • 1
    I would say only use out when you need the return value to decide if you should process the return data at all, as in TryParse, otherwise you should always return a structured object, as for if the structured object should be value type or a reference type depends on what additional use you make of the data – MikeT Oct 30 '13 at 15:48
  • @Lukas, Tuple is a class – MikeT Oct 30 '13 at 15:50

6 Answers6

115

They each have their pros and cons.

Out parameters are fast and cheap but require that you pass in a variable, and rely upon mutation. It is almost impossible to correctly use an out parameter with LINQ.

Tuples create collection pressure1 and are un-self-documenting. "Item1" is not very descriptive.

Custom structs can be slow to copy if they are large, but are self-documenting and are efficient if they are small. However it is also a pain to define a whole bunch of custom structs for trivial uses.

I would be inclined to the custom struct solution all other things being equal. Even better though is to make a function that only returns one value. Why are you returning two values in the first place?

Note that tuples in C# 7, which shipped six years after this answer was written, are value types and hence less likely to create collection pressure.


1 Every time you allocate a small object off the heap, that puts "pressure" on the garbage collector. The more pressure, the more frequent collections. In some applications is it important to control the amount of collection pressure produced, so allocating a few million tuples unnecessarily can be a bad thing in those applications. Of course, like all questions of performance, don't blindly make changes until you understand the magnitude of the problem.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    Returning two values is often a substitute for not having option types or ADT. – Anton Tykhyy Jun 17 '11 at 06:17
  • @Eric: This is about a `LoadBalancer` which returns the appropriate server address (`string` url) and the time this server address is valid (`int` minutes). Do you think something is wrong with design? (Note: Currently I use `out` keyword) – Xaqron Jun 17 '11 at 06:20
  • Eric, this one question has been bugging me for a while, are there any good use cases for Tuple type? – SolutionYogi Jun 17 '11 at 06:20
  • 3
    From my experience with other languages, I would say that generally tuples are used for quick and dirty grouping of items. It is usually better to create a class or struct simply because it allows you to name each item. When using tuples the meanings of each value can be hard to determine. But it does save you from taking the time to create the class/struct which can be overkill if said class/struct would not be used elsewhere. – Kevin Cathcart Jun 17 '11 at 12:14
  • 1
    @SolutionYogi: What he said. I typically use tuples for "glue" code; I probably wouldn't make a public interface that returned a tuple for example. – Eric Lippert Jun 17 '11 at 15:02
  • @Xaqron: A number of design points strike me. First, if "minutes" is the number of minutes for which this server address is going to be valid, then you need a *third* piece of information which is *when the clock started*. The "minutes" field is going to be read *in the future*, after all, and in the future there should be less time available. I would be more inclined to return the URL and *the time at which it expires*, not *the difference between the creation time and the expiry time*. Second, why return the url as a string when there's a perfectly good Uri class you could use? – Eric Lippert Jun 17 '11 at 15:05
  • 26
    @Xaqron: If you find that the idea of "data with a timeout" is common in your program then you might consider making a generic type "TimeLimited" so that you can have your method return a TimeLimited or TimeLimited or whatever. The TimeLimited class can then have helper methods that tell you "how long have we got left?" or "is it expired?" or whatever. Try to capture interesting semantics like this in the type system. – Eric Lippert Jun 17 '11 at 15:06
  • 3
    Absolutely, I would never use Tuple as part of public interface. But even for the 'private' code, I get enormous readability from a proper type instead of using Tuple (especially how easy it is to create a private inner type with Auto Properties). – SolutionYogi Jun 17 '11 at 15:11
  • 1
    You can have gain a bit of information if you use an alias directive for the Tupple class. e.g. using MyPair = Tuple; – Fraser Aug 25 '11 at 10:35
  • 1
    Returning multiple values ia a very common requirement, infact as system complexity increases they become the norm not the exception. – MikeT Oct 30 '13 at 15:43
  • Return an anonymous type with dynamic (NET 4.0) is an option – boctulus Jan 15 '15 at 22:08
  • From your answer, I gather tuples don't really have any pros. – Marc.2377 Jan 25 '19 at 20:40
  • 4
    @Marc.2377: Tuples certainly do have pros: they are the *logical* and *principled* way to combine two values into one. Reference tuples have the advantage of being copied by reference, which is fast. Value tuples have the advantage of being value types, which lessens collection pressure. There are lots of ways to solve this problem; in C# 7, the canonical way to do it is with a value tuple. – Eric Lippert Jan 25 '19 at 20:47
39

Adding to the previous answers, C# 7 brings value type tuples, unlike System.Tuple that is a reference type and also offer improved semantics.

You can still leave them unnamed and use the .Item* syntax:

(string, string, int) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.Item1; //John
person.Item2; //Doe
person.Item3;   //42

But what is really powerful about this new feature is the ability to have named tuples. So we could rewrite the above like this:

(string FirstName, string LastName, int Age) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.FirstName; //John
person.LastName; //Doe
person.Age;   //42

Destructuring is also supported:

(string firstName, string lastName, int age) = getPerson()

dimlucas
  • 5,040
  • 7
  • 37
  • 54
23

I think the answer depends on the semantics of what the function is doing, and the relationship between the two values.

For example, the TryParse methods take a out parameter to accept the parsed value, and return a bool to indicate whether or not the parse succeeded. The two values don't really belong together, so, semantically, it makes more sense, and the intent of the code is easier to read, to use the out parameter.

If, however, your function returns X/Y coordinates of some object on the screen, then the two values semantically belong together and it would be better to use a struct.

I'd personally avoid using a tuple for anything that will be visible to external code becuase of the awkward syntax for retrieving the members.

Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116
  • +1 for semantics. Your answer is more appropriate whit reference types when we can leave the `out` parameter `null`. There are a few nullable immutable types out there. – Xaqron Jun 17 '11 at 06:50
  • 3
    Actually, the two values in TryParse do very much belong together, far moreso than implied by having one as a return value and the other as a ByRef parameter. In many ways, the logical thing to return would be a nullable type. There are some cases where the TryParse pattern works nicely, and some where it's a pain (it's nice that it can be used in an "if" statement, but there are many cases where returning a nullable value or being able to specify a default value would be more convenient). – supercat Jun 20 '11 at 17:24
  • @supercat i would agree with andrew, they don't belong together. though they are related the return tells you if you need to bother with the value at all not something that needs to be processed in tandem. so after you have processed the return its no longer required for any other processing relating to the out value, this is diferent than say returning a KeyValuePair from a dictionary where there is a clear and on going link between the key and the value. though i agree if nullable types have been in .Net 1.1 they probably would have used them as null would be a proper way to flag no value – MikeT Oct 30 '13 at 15:57
  • @MikeT: I consider it exceptionally unfortunate that Microsoft has suggested that structures should only be used for things representing a single value, when in fact an exposed-field structure is the ideal medium for passing together a group of independent variables bound together with duct tape. The success indicator and the value are meaningful together *at the moment they are returned*, even if after that they would be more useful as separate variables. Fields of an exposed-field structure stored in a variable are themselves usable as separate variables. In any case... – supercat Oct 30 '13 at 16:20
  • @MikeT: Because of the ways covariance is and is not supported within the framework, the only `try` pattern which works with covariant interfaces is `T TryGetValue(whatever, out bool success)`; that approach would have allowed interfaces `IReadableMap : IReadableMap`, and let code which wants to map instances of `Animal` to instances of `Car` to accept a `Dictionary` [using `TryGetValue(TKey key, out bool success)`. No such variance is possible if `TValue` is used as a `ref` parameter. – supercat Oct 30 '13 at 16:26
  • @Supercat, i Agree that structures are rather too limiting on what they can do, to meet many of the things they would be perfect for. Just as an example when passing a complex object into a function where you want to edit it with out affecting the original, it would make more sense to use a struct than generate a clone function. or where a value object is editable but not nullable. however the immutable nature means that you can't change the value anyway to the class ends up being the only option. its these limitations that i think lead Microsoft to that recommendation – MikeT Oct 30 '13 at 16:31
  • @MikeT: Code which uses a type like `Point3d` will have to do two basic operations a lot: (1) make a storage location of some type encapsulate an instance which is slightly different from one it used to hold, without affecting what other storage locations encapsulate; (2) make a storage location of a type encapsulate a semantically-independent instance which is just like one in another storage location. Exposed-field structures do both operations well. Immutable class types are slightly better at #2, but immutable types in general are bad at #1. Mutable class types are bad at #2. – supercat Oct 30 '13 at 17:01
3

I will go with the approach of using Out parameter because in second approach you would require to create and object of Tuple class and then add value to it, which I think is an costly operation compared to returning the value in out parameter. Though if you want to return multiple values in Tuple Class (which infact can not be accomplished by just returning one out parameter) then I will go for second approach.

Sumit
  • 2,932
  • 6
  • 32
  • 54
  • I agree with `out`. Additionally there's a `params` keyword I did not mentioned to keep the question straight. – Xaqron Jun 17 '11 at 06:12
2

You did not mention one more option, which is having a custom class instead of struct. If the data has semantics associated to it which can be operated upon by functions, or the instance size is big enough (> 16 bytes as a rule of thumb), a custom class may be preferred. Use of "out" is not recommended in public API because of its association to pointers and requiring understanding of how reference types work.

https://msdn.microsoft.com/en-us/library/ms182131.aspx

Tuple is good for internal use, but it usage is awkward in public API. So, my vote is between struct and class for public API.

JeanP
  • 41
  • 4
  • 1
    If a type exists purely for the purpose of returning an aggregation of values, I'd say a simple exposed-field value type is the clearest fit for those semantics. If there's nothing in the type other than its fields, there won't be any question about what kinds of data validation it performs (none, obviously), whether it represents a captured or live view (an open-field structure can't act as a live view), etc. Immutable classes are less convenient to work with, and only offer a performance benefit if instances can be passed around multiple times. – supercat Aug 31 '16 at 22:07
1

There is no "best practice". It is what you are comfortable with and what works best in your situation. As long as you are consistent with this, there is no problem with any of the solutions you've posted.

foxy
  • 7,599
  • 2
  • 30
  • 34
  • 4
    Of course they all work. In case there's no technical advantage, I'm still curious to know what is mostly used by experts. – Xaqron Jun 17 '11 at 06:10