Any reason to use out parameters with the C# 7 tuple return values? [closed]
Asked Answered
C

2

12

I have just watched a video presenting the new features of C# 7. Among others, it introduces the possibility to return a tuple type (e.g.: (int, int), which, I believe, is just a syntactic sugar for Tuple<int, int>). Thus, if we have a method returning multiple values, there are 3 possibilities in C# on how to do it:

(int first, int second) ReturnTuple()
{
    return (1, 2);
}

int ReturnOutParam(out int second)
{
    second = 2;
    return 1;
}

CustomObject ReturnObject()
{
    return new CustomObject
    {
        First = 1,
        Second = 2
    };
}

I believe, there are no more ways to do it - if yes, please, correct me.

Which of those three methods is the right one? And in which cases can we use the remaining two? With every new C# release, I have the feeling that there are one or two design patterns that just became obsolete in the .NET world. Some features were very useful (like generics, partials, LINQ, lambdas, async/await or null propagator). Others are very situational (dynamic, nameof). And then there are those that just don't make sense to me (property auto-initializers, tuple return values or local functions).

Cooksey answered 12/12, 2016 at 21:25 Comment(4)
"Which of those three methods is the right one?" well, they all work, so in that sense they are all "right". Which one is actually right for a particular scenario will depend on the scenario. Having said that, the second one seems really awkward.Granville
You could ask the same question about normal parameters. Why declare a method as f(int a, int b, int c) instead of f(Tuple<int, int, int> abc) or f(CustomClass abc)? If the values are logically grouped (eg, you would consider making a class out of them if they were used more often), then use the tuple. If not, separate them out.Hardy
the 3rd method you show forces you to define a class just your need to return 2 values (and maybe use it nowhere else in your program). out parameters.... I always disliked them (can't tell you why...) Language support for returning multiple values seems a good thing to me. Of course you can do the same in the old way if you preferCarmoncarmona
There is an additional scenario to consider. If the out parameter is optional, then modelling the return as a tuple forces you to change the method name signature, since methods cannot differ by only the return type. Using an out parameter lets you support an extra optional return value without creating a new method name.Tennyson
W
18

First and foremost, a tuple like (int, int) is syntactic sugar for ValueTuple<int, int>. The differences between Tuple and ValueTuple are:

  • ValueTuple is a value type, so there's no need to allocate an object on the heap
  • ValueTuple is mutable
  • ValueTuple has (obviously) built-in language support, and it lets you name the tuple items through a custom attribute (TupleElementNamesAttribute). With Tuple, you'll only get Item1, Item2 etc.

With each new language version some features become obsolete. For instance, the delegate { } syntax was superseded by lambdas. You could argue that out parameters fits in this category, but that's subjective. Yet all the features need to stay there for backwards compatibility.

For instance, bool int.TryParse(string input, out int value) should have become int? int.TryParse(string input) with the inclusion of nullable value types to the language, but the old function was already there in the framework, so it had to stay.

My rule of thumb would be: use value tuples for private methods or utility functions, but prefer full-fledged structs for any public API, it just feels cleaner this way. I generally avoid out params except occasionally for private methods.

I don't get why some of these new features don't make sense to you:

  • Property auto-initializers are finally there, they should have been implemented along with auto-properties in the first place, but were probably skipped because of time constraints. It's an obvious win for code readability, and it adds a feature that was always there for fields.
  • Tuple return values is the topic of your question, it's also an obvious readability win over the old tuples. Being able to name your tuple items speaks for itself.
  • Local functions will mostly prove useful for implementing the iterator pattern, but I occasionally wished to have such a feature and worked around it by using a lambda.
Whaling answered 12/12, 2016 at 21:39 Comment(0)
W
9

For me it still depends on the situation. For example for TryParse it's still more readable to do:

if (int.TryParse("123", out var i))
{
   // i
}

than

var (success, i) = int.TryParse("123");
if (success)
{
   // i
}

and in most cases returning more than one value from method is a code smell. You should always wrap what you return into class which describes your values better than First and Second.

Worry answered 12/12, 2016 at 21:31 Comment(3)
Worth bearing in mind that if int.TryParse returned a tuple you could do if (int.TryParse("123") is var (success, i) && success) { // use i here...Firstrate
It's just lack of normal pattern matching. Take a look on Rust, which is dealing very good with such constructsArdys
using out var i is kinda nice, i didnt know that would workMillwright

© 2022 - 2024 — McMap. All rights reserved.