32

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.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
bwerks
  • 8,651
  • 14
  • 68
  • 100

10 Answers10

35

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.

Community
  • 1
  • 1
dtb
  • 213,145
  • 36
  • 401
  • 431
  • 26
    I guess Cwalina and Abrams weren't consulted when the `TryParse` methods were designed. :) – David Hoerster Aug 21 '10 at 23:31
  • 2
    so is IDictionary.TryGetValue method. It's not that convincing if .NET framework doesn't follow their suggestion. – treehouse Aug 21 '10 at 23:35
  • 6
    @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 Aug 21 '10 at 23:38
  • 2
    Of course, for those of us who *do* understand pointers, ref is an invaluable addition for efficiency in some circumstances - TryParse & Swap are great examples. The rule should really be "consider other designs rather than blindly using ref everywhere, especially if you don't really understand what ref means". – Jason Williams Aug 22 '10 at 20:07
  • 8
    TryParse take an out parameter, not ref--that's the whole point. – bwerks Aug 24 '10 at 05:33
  • Well if you design for generic programming you may need it. An example: if you write Foo (T input) { T = this.x; } the behavior will differ if you call Foo and Foo , with ref you'll have same result. – kappa Sep 20 '12 at 13:52
  • 2
    That is a terrible assumption of a general dumbing-down among engineers. – user1725145 Nov 06 '13 at 12:51
15

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

Timwi
  • 65,159
  • 33
  • 165
  • 230
12

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
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

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
  • 56,972
  • 11
  • 108
  • 137
  • 1
    what is the benefit of using ref when compared to out? – silvo Aug 21 '10 at 22:32
  • 2
    out means the object has to be initialized first, with ref, it doesn't have to be. – Malfist Aug 21 '10 at 22:44
  • 13
    @Malfist I think you have that backwards. – siride Aug 21 '10 at 22:53
  • 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 Aug 21 '10 at 23:35
  • Out can only be used to *initialise* variables. ref needs to be used if you wish to *update* variables during their lifetime. – Jason Williams Aug 22 '10 at 07:14
  • @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 Aug 22 '10 at 07:17
  • 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 Aug 24 '10 at 05:48
  • @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 Aug 24 '10 at 15:29
  • @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 Aug 25 '10 at 03:23
  • 1
    @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 Aug 25 '10 at 05:41
2

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.

Lasse Espeholt
  • 17,622
  • 5
  • 63
  • 99
  • Oh man, that nullable on TryParse will make a hell of an extension method, I bet. – bwerks Aug 21 '10 at 23:39
  • 2
    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 Aug 21 '10 at 23:46
  • @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. – Lasse Espeholt Aug 22 '10 at 08:26
  • 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 Aug 23 '10 at 20:37
1

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
  • 77,689
  • 9
  • 166
  • 211
0

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

Charles
  • 11,269
  • 13
  • 67
  • 105
0

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
  • 5,108
  • 4
  • 30
  • 52
-1

Another useful example in addition to swap<> is this:

Prompter.getString("Name ? ", ref firstName);
Prompter.getString("Lastname ? ", ref lastName);
Prompter.getString("Birthday ? ", ref firstName);
Prompter.getInt("Id ? ", ref id);
Prompter.getChar("Id type: <n = national id, p = passport, d = driver licence, m = medicare> \n? ", ref c);



public static class Prompter
{
    public static void getKey(string msg, ref string key)
    {
        Console.Write(msg);
        ConsoleKeyInfo cki = Console.ReadKey();
        string k = cki.Key.ToString();
        if (k.Length == 1)
            key = k;
    }

    public static void getChar(string msg, ref char key)
    {
        Console.Write(msg);
        key = Console.ReadKey().KeyChar;
        Console.WriteLine();
    }

    public static void getString(string msg, ref string s)
    {
        Console.Write(msg);
        string input = Console.ReadLine();
        if (input.Length != 0)
            s = input;
    }

    public static void getInt(string msg, ref int i)
    {
        int result;
        string s;

        Console.Write(msg);
        s = Console.ReadLine();

        int.TryParse(s, out result);
        if (result != 0)
            i = result;       
    }

    // not implemented yet
    public static string getDate(string msg)
    {
        // I should use DateTime.ParseExact(dateString, format, provider);
        throw new NotImplementedException();
    }    


}

Use out here it's not an option

boctulus
  • 404
  • 9
  • 15
-1

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
  • 2,222
  • 17
  • 20
  • 12
    You should be using the `out` keyword when returning multiple values. `ref` really wouldn't be appropriate here. – Jeff Mercado Aug 21 '10 at 22:29
  • 3
    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 Aug 21 '10 at 23:12
  • 2
    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 Aug 21 '10 at 23:33
  • In my case I'm handing over objects and change data in them. My example here would be better with out. – Andreas Rehm Aug 22 '10 at 09:02
  • 1
    @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 Aug 22 '10 at 22:00
  • 2
    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 Aug 23 '10 at 08:32