tags:

views:

1191

answers:

8

I'm trying to formalise the usage of the "out" keyword in c# for a project I'm on, particularly with respect to any public methods. I can't seem to find any best practices out there and would like to know what is good or bad.

Sometimes I'm seeing some methods signatures that look like this:

public decimal CalcSomething(Date start, Date end, out int someOtherNumber){}

At this point, it's just a feeling, this doesn't sit well with me. For some reason, I'd prefer to see:

public Result CalcSomething(Date start, Date end){}

where the result is a type that contains a decimal and the someOtherNumber. I think this makes it easier to read. It allows Result to be extended or have properties added without breaking code. It also means that the caller of this method doesn't have to declare a locally scoped "someOtherNumber" before calling. From usage expectations, not all callers are going to be interested in "someOtherNumber".

As a contrast, the only instances that I can think of right now within the .Net framework where "out" parameters make sense are in methods like TryParse(). These actually make the caller write simpler code, whereby the caller is primarily going to be interested in the out parameter.

int i;
if(int.TryParse("1", i)){
  DoSomething(i);
}

I'm thinking that "out" should only be used if the return type is bool and the expected usages are where the "out" parameters will always be of interest to the caller, by design.

Thoughts?

+20  A: 

There is a reason that one of the static code analysis (=FxCop) rules points at you when you use out parameters. I'd say: only use out when really needed in interop type scenarios. In all other cases, simply do not use out. But perhaps that's just me?

peSHIr
Just found this: http://msdn.microsoft.com/en-us/library/ms182131.aspx, CA1021: AvoidOutParameters. FxCop rule... good enough for me. Thank you.
Damian Hickey
+1  A: 

Stay away from out. It's there as a low-level convenience. But at a high level, it's an anti-technique.

int? i = Util.TryParseInt32("1");
if(i == null)
    return;
DoSomething(i);
Justice
Why is that voted up? Tried compiling this? TryParse()-Method returns a boolean value for determination if parsing succeeds, to not use Exceptions for program-steering - and that is good like this.
BeowulfOF
@Justice: Not entirely sure what you're saying with your example. TryParse() is not implemented like that in the .NET Framework.
Mitch Wheat
I think it was an example of how it _should_ have been implemented. +1
Eyvind
Changing int.TryParse to Util.TryParseInt32.
Justice
@Eyvind, that is definitely how I would have liked int.TryParse to have been implemented.
Justice
+4  A: 

Your approach is better than out, because you can "chain" calls that way:

DoSomethingElse(DoThing(a,b).Result);

as opposed to

DoThing(a, out b);
DoSomethingElse(b);

The TryParse methods implemented with "out" was a mistake, IMO. Those would have been very convenient in chains.

Giovanni Galbo
Well, you need a way to tell if the parsing succeeded.
lacop
+2  A: 

One advantage of out is that the compiler will verify that CalcSomething does in fact assign a value to someOtherNumber. It will not verify that the someOtherNumber field of Result has a value.

James Curran
My approach here would be to enforce the someOtherNumber field to be initialized via the Result constructor.
Damian Hickey
FYI - only the C# compiler will do this. You can flagrantly break this contract in other .NET languages.
plinth
+3  A: 

There are only very few cases where I would use out. One of them is if your method returns two variables that from an OO point of view do not belong into an object together.

If for example, you want to get the most common word in a text string, and the 42nd word in the text, you could compute both in the same method (having to parse the text only once). But for your application, these informations have no relation to each other: You need the most common word for statistical purposes, but you only need the 42nd word because your customer is a geeky Douglas Adams fan.

Yes, that example is very contrived, but I haven't got a better one...

Treb
Even then, I would use an class/struct (or tuple instance from 4.0 onwards). Something like the contrived `MostCommonAnd42thWordResult`, but still. Or of course design the entire logic differently, to work around the "contrividness" and "do not belong into an object together-feeling" of this example. ;-)
peSHIr
A: 

You could create a generic tuple class for the purpose of returning multiple values. This seems to be a decent solution but I can't help but feel that you lose a bit of readability by returning such a generic type (Result is no better in that regard).

One important point, though, that james curran also pointed out, is that the compiler enforces an assignment of the value. This is a general pattern I see in C#, that you must state certain things explicitly, for more readable code. Another example of this is the override keyword which you don't have in Java.

Morten Christiansen
+8  A: 

This is what the .NET Framework Developer's Guide has to say about out parameters:

Avoid using out or reference parameters.

Working with members that define out or reference parameters requires that the developer understand pointers, subtle differences between value types and reference types, and initialization differences between out and reference parameters.

But if you do use them:

Do place all out parameters after all of the pass-by-value and ref parameters (excluding parameter arrays), even if this results in an inconsistency in parameter ordering between overloads.

This convention makes the method signature easier to understand.

Mitch Wheat
So the Developer's Guide is basically saying that you shouldn't use out parameters because most developers are too stupid to understand a very elementary aspect of their chosen language!!
Stephen Martin
Stephen, that is so true. I say use them where you need them.
Lamar
A: 

If your result is more complex than a single value, you should, if possible, create a result object. The reasons I have to say this?

  1. The entire result is encapsulated. That is, you have a single package that informs the code of the complete result of CalcSomething. Instead of having external code interpret what the decimal return value means, you can name the properties for your previous return value, Your someOtherNumber value, etc.

  2. You can include more complex success indicators. The function call you wrote might throw an exception if end comes before start, but exception throwing is the only way to report errors. Using a result object, you can include a boolean or enumerated "Success" value, with appropriate error reporting.

  3. You can delay the execution of the result until you actually examine the "result" field. That is, the execution of any computing needn't be done until you use the values.

Tom Moseley