I've been playing with returning tuples from methods, so method signatures like this...:
private int GetAlpha() {...} // -1 indicates an error
private bool GetAlpha(out int alphaOut) {...}
...turn into this:
private (bool Success, int Alpha) GetAlpha() {...}
PROS:
- I like avoiding the use of out-of-band values like
-1
to signal an error to the caller - I've been reading recently that
out
parameters are evil and to be avoided
CONS:
- I can only use the
var (success, a, b, ...) = Foo(...)
syntax ONCE in a typical method. Thereafter, I'm forced to declare nearly all variables in the returned tuple, because 'success' is already defined - I not only have to declare return variables, but I also have to explicitly specify their type (vs. implicitly using
var
).
var (success, alpha) = GetAlpha();
if (success)
{
//var (success, beta, gamma) = GetStuff(alpha); // ERROR - 'success' is already defined
//(success, beta, gamma) = GetStuff(alpha); // ERROR - 'beta' and 'gamma' are undefined
//(success, var beta, var gamma) = GetStuff(alpha); // ERROR - no such syntax, silly!
string beta;
DateTime gamma;
(success, beta, gamma) = GetStuff(alpha);
.
.
.
The convenience and conciseness of using the implicit declaration and typing is so nice, that it bugs me I'm typically only able to use it once in a method.
Am I missing anything? Is there some other pattern that avoids this?
CodePudding user response:
I think there's an argument to be made that it's dangerous to reuse variables the way you're trying to. I'd personally prefer to see this:
var (alphaSucceeded, alpha) = GetAlpha();
if (alphaSucceeded)
{
var (getStuffSucceeded, beta, gamma) = GetStuff(alpha);
There are many ways to solve this kind of problem.
For example, you could use a monadic library (there are many out there: this is mine) to represent the concept of GetAlpha()
returning a non-value.
var alphaMaybe = MaybeGetAlpha();
var stuff = alphaMaybe.Select(alpha => GetStuff(alpha)); // or just.Select(GetStuff)
Or, since you're currently only dealing with value types, you could just use Nullable<>
s. In later versions of C#, you can use nullables with reference types, and also use pattern matching to simplify syntax.
if(GetAlpha() is int alpha)
{
// use alpha
}
CodePudding user response:
I would argue this particular case - where you are interested if the call is successful or not - is a case where out
is best suited.
private bool GetAlpha(out int alphaOut) {...}
I would use the tuple approach only if I'm not interested in the success state.
private (int, string) GetAlpha() {}
If I need the success state AND I need multiple returns, then I would use both so that I don't have multiple out
params - which I think is your concern.
private bool GetAlpha(out (int, string) tupleAlpha) {...}
BTW, I don't think there is anything wrong with out
. Yes, if you are using it with void methods its unnecessary, or if you are returning multiple out
(but that's more of a code smell indicating your function is doing too many things). There are plenty of examples of the out
pattern in the .NET
API.
https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-6.0