views:

458

answers:

11

The more I see ref used in production code, the more misuse I encounter and the more pain it causes me. I have come to hate this keyword, because from a framework-building standpoint, it seems silly. When would it be a good idea to communicate to users of your code the notion of maybe changing an object reference/value out from beneath them?

By contrast, I love out keywords and I love even more when no keywords are used at all, in both cases because of the guarantees you're given when using them. Ref on the other hand makes no guarantees, except that you'll be forced to initialize the parameter before you pass it in, even though nothing may be changed about it.

I'm no sage developer though; I'm sure it's got practically applicable uses. I'd just like to know what they are.

A: 

I'm using ref quite often. Just think about functions with multiple return values. It doesn't make sense to create a return object (helper object) or even using hashtables for this purpose.

Example:

 getTreeNodeValues(ref selectedValue, ref selectedText);

Edit:

It's better to use out here - as commented.

 getTreeNodeValues(out selectedValue, out selectedText);

I'm using it for processing objects:

MyCar car = new MyCar { Name="TestCar"; Wieght=1000; }

UpdateWeight(ref car, 2000);
Andreas Rehm
You should be using the `out` keyword when returning multiple values. `ref` really wouldn't be appropriate here.
Jeff M
You should definitely use `out` as indicated. But you should also consider the option of using a helper object. If you have more than two return values, it tends to be the cleaner strategy.
Timwi
I think that's a good example for the claim `the difference between out and ref parameters is not widely understood` made in dtb's answer.
Heinzi
In my case I'm handing over objects and change data in them. My example here would be better with out.
Andreas Rehm
@Andreas: Is MyCar a class (rather than a struct)? If yes, then I'm pretty sure that you *don't* need the `ref` keyword here. It is *not* required for changing the properties of an object based on a class (= a *reference type*).
Heinzi
As @Heinzi says, if you just want to change the state of the reference type you shouldn't use the ref keyword, it is not needed. If you use ref on a reference type parameter your method can actually replace the entire object with a completely new object or set it to null. Unless you really need this it is misleading to callers and can introduce confusing bugs.
Ash
+4  A: 

Any time you want to change the value of a value type - this happens a lot in cases where you want to efficiently update a pair of related values (i.e. rather than returning a struct containing two ints, you pass (ref int x, ref int y))

Jason Williams
what is the benefit of using ref when compared to out?
silvo
out means the object has to be initialized first, with ref, it doesn't have to be.
Malfist
@Malfist I think you have that backwards.
siride
My response to this is that in this case, just use out. If you want change, declare a new variable and pass that in, perhaps later assigning the returned value over the one you already have. Others who try to read your code later will sing your praises for generations.
bwerks
Out can only be used to *initialise* variables. ref needs to be used if you wish to *update* variables during their lifetime.
Jason Williams
@bwerks: initialising a new variable is very inefficient and clumsy compared to just updating your existing variable in place. Especially if you're talking about 2 or 3 variables.
Jason Williams
Wait, initializing a new variable is very inefficient, and thus using ref, where you're required to initialize even before calling the method, is better? Furthermore, you're wrong about out. There's nothing preventing you from passing an initialized variable as an out parameter. Outs may be initialized or uninitialized; refs must be initialized. The only time ref is actually required over out is when you want to set the parameter to a new value derived from its own old value.
bwerks
@bwerks: I'm talking about updating (overwriting) an existing variable with a new value (in place), not just calling a method once to initialise a variable. As for "you can pass an initialised variable with out", try compiling **public void Set(out int a) { a += 7; }** and you'll learn that there is a rather important difference between out and ref.
Jason Williams
@Jason Williams: yes, that doesn't compile, but it doesn't compile because you are trying to update a variable, rather than initialize it. That's what bwerks is talking about. If you don't need to update the variable, only initialize it, then there's no point in using ref.
siride
@siride: Of course. But this answer (and the question) is about where/why you might use **ref** - in this case, for **updating** an existing variable. My Set() example shows a situation where you *must use ref rather than out*.
Jason Williams
+14  A: 

The Framework Design Guidelines (a book by Krzysztof Cwalina and Brad Abrams) recommend to avoid both ref and out parameters.

AVOID using out or ref parameters.

Using out or ref parameters requires experience with pointers, understanding how value types and reference types differ, and handling methods with multiple return values. Also, the difference between out and ref parameters is not widely understood. Framework architects designing for a general audience should not expect users to master working with out or ref parameters.

The Framework Design Guidelines cite the canonical Swap method as a valid exception:

void Swap<T>(ref T obj1, ref T obj2)
{
    T temp = obj1;
    obj1 = obj2;
    obj2 = temp;
}

but at the same time a comment remarks

Swap always comes up in these discussions, but I have not written code that actually needed a swap method since college. Unless you've got a very good reason, avoid out and ref altogether.

dtb
I guess Cwalina and Abrams weren't consulted when the `TryParse` methods were designed. :)
David Hoerster
so is IDictionary<TKey, TValue>.TryGetValue method. It's not that convincing if .NET framework doesn't follow their suggestion.
Kai Wang
@D Hoerster - actually the book discusses the TryParse pattern in a positive light. It's an "avoid" guideline, meaning there are known cases where breaking the rule makes sense.
TrueWill
Jason Williams
TryParse take an out parameter, not ref--that's the whole point.
bwerks
A: 

It's useful when you need efficient in-place algorithms on bignums.

Charles
Have you profiled and determined that that's your performance bottleneck?
TrueWill
@TrueWill: No, the group that wrote the bignum software did that.
Charles
+2  A: 

Maybe when you have a struct (which is a value type):

struct Foo
{
    int i;

    public void Test()
    {
        i++;
    }
}

static void update(ref Foo foo)
{
    foo.Test();
}

and

Foo b = new Foo();
update(ref b);

Here you would to use two-parameters with out like:

static void update(Foo foo, out Foo outFoo) //Yes I know you could return one foo instead of a out but look below
{
    foo.Test();

    outFoo = foo;
}

imaging the method having more than one Foo then you would get twice the parameters with out versus ref. An alternative is to return a N-tuple. I don't have a real-world example on when to use this stuff.

Add on: Different .TryParse methods could also have avoided out if they returned Nullable<T> instead which essentially is a tuple of boolean * T.

lasseespeholt
Oh man, that nullable on TryParse will make a hell of an extension method, I bet.
bwerks
Returning an object or null and expecting the caller to check is risky. We've got older APIs that do that, and I've seen too much client code that immediately calls methods on the returned "object." Throwing on failure and/or the TryParse pattern are generally better options. (TryParse doesn't eliminate foolish mistakes, but it does discourage them.)
TrueWill
@bwerks I have done that. @TrueWill You may be correct, but you have to use `??` so users will know. I have used the extension method like: `jf (Request["SomeHeader"].ToBoolean() == true)` which I really think is nice. It also works when the header is null but it may not be that clear that it does.
lasseespeholt
I've actually assumed a convention surrounding "try" methods that always return bool, have an out parameter, and never throw exceptions; it's actually quite handy. However, after thinking about it, this convention is violated by conflating the return value and out parameter since null is occasionally a valid response, in which case the previous form would have return null in the out parameter with the bool return value equal to true. Example: Dictionary.TryGetValue(key) after Dictionary.Add(key, null).
bwerks
+5  A: 

I try to avoid it on public APIs, but it definitely has uses. Mutable value-types is an important one, especially on things like CF (where mutable structs are more common, due to platform requirements). However, perhaps the most common time I use it is when refactoring parts of a complex algorithm out into a few methods, where a state object is overkill and I need to pass multiple values around:

i.e.

var x = .....
var y = .....
// some local code...
var z = DoSomethingSpecific(ref x, ref y); // needs and updates x/y
// more local code...

etc. Where DoSomethingSpecific is a private method, just moved out to keep method responsibility manageable.

Marc Gravell
+8  A: 

Most of the Interlocked methods use ref parameters for (I’m sure you agree) good reason.

Timwi
Touche. Going to upvote this one.
bwerks
A: 

I have an example of a situation where - in my eyes - ref provides the cleanest solution:
In one of my projects I make rather extensive use of filters for the optional modification of some data... So basically I have an Interface IFilter<T> with T being the type of object to be filtered, which all my various filter classes implement. My IFilter<T> only has one method: void Filter(ref T element). My only alternative would be to have a function T Filter(T element). However, it is completely up to the implementation to decide whether to modify an existing object or to create a new one. Consequently ref is the cleanest approach.

blublubblub
T Filter(T element) would be a more functional approach, and would allow for immutable types and thread safety.
TrueWill
A: 

Hypothetically, I'd guess that you might use a lot of ref/out arguments if you intended to mimic the architecture of older procedural software, for example old game engines and so on. I've scanned the source code of one, I think it was Duke Nukem 3D, and it's procedural with lots of subroutines modifying variables in place, and almost no functions. Obviously, you'd be unlikely to program like this for a real production application unless you had some specific aim in mind.

Tom W
A: 

How about if one wishes to pass an array to a function which might or might not change its size and do something else to it. Often, one would wrap the array in another object, but if one wishes to handle the array directly passing by reference would seem the most natural approach.

supercat
A: 

I use ref in a current project when passing COM objects around because I don't want a new COM object implicitly created by the callee. My approach is that any method that creates an COM object is responsible for properly releasing it. From what I understand, doing COM interop can have nasty side effects when COM objects aren't properly released.

Nick Hebb