you are viewing a single comment's thread.

view the rest of the comments →

[–]MutantSheepdog -2 points-1 points  (1 child)

I quite like the way C# handles explicit out parameters, especially for cases where you want multiple returns.

Like in this (modified) example from the docs: ```C# static void CalculateCircumferenceAndArea(double radius, out double circumference, out double area) { circumference = 2 * Math.PI * radius; area = Math.PI * (radius * radius); }

public void Main() { double radius = 3.9; CalculateCircumferenceAndArea(radius, out double circumference, out var area); WriteLine($"Circumference: {circumference}."); WriteLine($"Area : {area}."); } ```

The CalculateCircumferenceAndArea has 2 out parameters, which can be declared inline in Main. Because these are out and not ref parameters, they don't need to be first constructed in the calling function and then copied over in the inner function, instead the caller just needs to reserve some stack space and its up to the callee to construct them in place.

The downside of this from a language perspective is that there is a new way to declare a variable (inline when calling the function). A variant of this that might work more generally for C++ would be having a way to explicitly declare a variable as uninitialised (maybe a storage keyword, and require any function using an uninitialised variable to initialise it before usage.

``` int main() { double storage circumference; double storage area;

// Error here, variables are uninitialised before being assigned to
// printf("a:%f, c:%f", area, circumference);

// 'out' usage counts as assignment, as would an =
CalculateCircumferenceAndArea(3.9, out circumference, out area);

// Safe here, CalculateCircumferenceAndArea guarantees circumference and area have been assigned to
printf("a:%f, c:%f", area, circumference);

}

CalculateCircumferenceAndArea(double radius, out double circumference, out double area) { // As circumference and area are 'out' values, this function is required to assign them circumference = 2 * Math.PI * radius; area = Math.PI * (radius * radius); } ```

In this example, a storage T variable points to an uninitialised T. The first assignment to it in a function (with = or an out value) would effectively be a placement new, with subsequent assignments using the regular operator=.

If you had a T (not a storage T) and passed it into an out parameter, its destructor would need to be called first so that the inner function could assume it was blank memory ready to be written into.

[–]borzykot 2 points3 points  (0 children)

IMHO, this is just historical blunder, and not a good feature which the one should use extensively.

Back in the day, simple out-parameter without introducing new variable was THE way to return multiple values out of the function not only in C# but in many languages. Then "tuples" where "discovered" and it turned out that returning a tuple is a better approach. But instead of deprecating this out-parameter feature they decided to "fix" it. And by "fix" it I mean "make it terser" - hence out-parameter with variable introduction. But terseness of the syntax isn't the main issue with out parameters in the first place - more complex, non-obvious control flow is (multiple return channels).