Value tuples exist, so why use the "out" parameter modifier? [closed]
Asked Answered
J

3

4

Microsoft's documentation for the out parameter modifier points out the following:

Declaring a method with out arguments is a classic workaround to return multiple values. Consider value tuples for similar scenarios.

This strikes me as a remarkably good point. What use case remains for out, now that we have value tuples?

Juliajulian answered 15/10, 2022 at 21:2 Comment(2)
The TryDoSomething pattern (returns Boolean true on success, with the output in anout parameter) relies on it. It's been around since day 1 (I think double.TryParse was in v1.0 of the Framework)Sloan
Does this answer your question? Returning two values, Tuple vs 'out' vs 'struct'Airy
I
7

One major use case I can think of off the top of my head are Try... methods that return a value and a boolean so that you can check whether the action succeded or not:

// With out parameters:
if(int.TryParse(someString, out int result)){
   // do something with the int
}

// With tuples:
var (success, value) = int.TryParseWithTuples(someString);
if(success){
   // do something with the int
}

With the out parameter, the notation is cleaner and results in less lines (and doesn't require you to create a local variable for the success boolean). It also allows you to do this:

if(int.TryParse(someString, out int r1)){
   // do something with the int
} else if(int.TryParse(fallbackString, out int r2)){
   // do something with the fallback int
} else {
   throw new InvalidOperationException();
}

With tuples, this would look like this:

var (success, value) = int.TryParseWithTuples(someString);
if(success){
   // do something with the int
} else {
   (success, value) = int.TryParseWithTuples(fallbackString);
   if(success){
       // do something with the fallback int
   } else {
       throw new InvalidOperationException();
   }
}
Inmesh answered 15/10, 2022 at 21:7 Comment(0)
I
1

In addition to the ease of use of the TryX pattern, as pointed out by ascpixi, another case that makes a value-tuple unsuitable as a return type is when one of the two values is a reference. For example take a look at the CollectionsMarshal.GetValueRefOrAddDefault API:

public static ref TValue? GetValueRefOrAddDefault<TKey,TValue> (
    Dictionary<TKey,TValue> dictionary,
    TKey key,
    out bool exists);

It is used like this:

ref int refValue = ref CollectionsMarshal.GetValueRefOrAddDefault(
    dictionary, key, out bool exists);

if (!exists) refValue = 1; else refValue++;

The ValueTuple<T1, T2> is not a ref struct, so it could not be used for this API. Theoretically this API could return a custom tuple-like ref struct, but in that case it would lack the language support that exists only for real value-tuples, like the shorthand notation with parentheses etc.

Irmgardirmina answered 15/10, 2022 at 22:16 Comment(0)
S
0

As a semi-escoteric case, there may be cases where the size of a tuple may have different passing semantics compared to an out. Sharplab shows different ASM for out vs ValueTuple which tends me towards thinking there are performance-sensitive cases where out would be better.

Another case that comes to mind, before 'named tuples' became a thing, was that the signature with out args could be more descriptive as to the values.

As the docs suggest, normally modern, 'named' ValueTuples are very composable and often great for app-level behavior. But for the reasons mentioned above, it was easier to make compiler sugar around some constructs for transition's sake.

Stilla answered 15/10, 2022 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.