Professor Mustard Programming
 

Programming 101 - Unit 03 - Modularity

03c) Ref and Out
 
You might have noticed that any changes you make to a parameter in a method will not affect the original one that was passed in. For example, consider the following method:

static void DoStuff(int num)
{
  num = 10;
}



This method doesn't actually accomplish anything, because "num" is only a copy of the original variable's value - it's not the original variable (this is called "passing by value"). So, all the method really does is change the value of a variable that will disappear in another line or two, anyway. But what if we actually want the method to operate directly on the original variable? Well, that's what the "ref" keyword is for...

static void DoStuffWithRef(ref int num)
{
  num = 10;
}



In this method, the "num" parameter is preceded by the "ref" keyword, which means that we are passing the variable by reference. In other words, we haven't passed in the variable's value, we've passed in a reference to the variable itself. Any changes that this method makes to "num" will affect the original variable that was passed in, as well. If you don't believe me, try calling the two methods, one after another, and watch what happens to the variable that is passed in (and note that we also have to use the "ref" keyword when calling the method, not just when defining the method's signature):

int numWithoutRef = 5;
int numWithRef = 5;
DoStuff(numWithoutRef);
DoStuffWithRef(ref numWithRef);

Console.WriteLine("numWithoutRef: " + numWithoutRef);
Console.WriteLine("numWithRef: " + numWithRef);



This code snippet will yield the following output:

numWithoutRef: 5
numWithRef: 10



So there you go; proof that "ref" works. And yes, I called my method "DoStuff()", even though I said it was a terrible method name in an earlier lesson. Shut up.

Besides allowing you to directly operate on a variable from within a method, "ref" also opens up the possibility of getting multiple "return types" from one method. How so? Well, consider this:

static void GetLotsOfResults(ref int num, ref string msg, ref bool logic)
{
  num = 20;
  msg = "changed in method";
  logic = true;
}



Our "GetLotsOfResults()" method has three datatypes, all of which are passed in by reference. We thus have the ability to change all three original variables from within this method. On a larger scale, there's really no limit to the number of "return types" we can have in a method, when employing this keyword. You also can mix and match "ref" parameters with "non-ref" parameters, just as you can mix and match parameters of different datatypes.

There's a better keyword for purely "output" parameters, however, and that is "out". "out" and "ref" work in very similar ways; in many cases they can even be used interchangeably. Once difference is the intent of the programmer: if you're just trying to share a variable between a method and its caller, you'd probably use "ref". If you want a method that "returns" multiple variables, as shown above, you'd probably go with "out". Also, "out" assumes that the original value of the variable you pass into the method is expendable, since we're planning to give it a new value inside the method (otherwise, why even bother with the "out" keyword at all?). As such, you don't even have to initialize a variable before passing it into a method's "out" parameter.

Before you go nuts and start using "ref/out" all over the place, though, I should sound a few words of caution. "ref" is not intended to be used as replacement for method return types. Indeed, there are a few things that I've never really liked about "ref", the main one being that your method is no longer a safe, separate little island from the main program. It's just an imaginary boundary line that extends the main program, which makes bugs more likely, and harder to catch. For example, if your method chokes on something halfway through its execution, and has to fall back on your main program's try/catch, what state will those "ref" variables be in? It's hard to say. When we're not dealing with "ref" variables, however, we don't have to worry about such things. We just let the method die, confident that anything that happened in the method no longer matters to the rest of the program. "out" is a slightly different matter, since we don't care what the original value of the variable was, anyway. Even so, it kind of breaks the simple "tidiness" of using normal parameters for "questions", and return values for "answers". And later on, you'll learn about structs, which are lightweight containers that could be used to return as many variables as you want in method's actual return value.

Of course, you shouldn't avoid using "ref/out". If you need to get multiple results from one method, or actually want to directly operate on the variable you're passing in, a "ref" variable is exactly what you need. Just keep it in its place, like any other programming mechanism, and you should be fine.



using System;

namespace C_Ref
{
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      // Demonstrate difference between "ref" and without "ref"
      int numWithoutRef = 5;
      int numWithRef = 5;
      DoWithoutRef(numWithoutRef);
      DoWithRef(ref numWithRef);

      Console.WriteLine("numWithoutRef: " + numWithoutRef);
      Console.WriteLine("numWithRef: " + numWithRef);

      // Get multiple "return" values from one method call
      int num = 1;
      string msg = "old message";
      bool logic = false;

      Console.WriteLine(Environment.NewLine + "Before method call:");
      Console.WriteLine("num: " + num);
      Console.WriteLine("msg: " + msg);
      Console.WriteLine("logic: " + logic);

      GetLotsOfResultsFromOneMethod(out num, out msg, out logic);

      Console.WriteLine(Environment.NewLine + "After method call:");
      Console.WriteLine("num: " + num);
      Console.WriteLine("msg: " + msg);
      Console.WriteLine("logic: " + logic);

      PromptForExit();
    }

    static void DoWithoutRef(int num)
    {
      num = 10;
    }

    static void DoWithRef(ref int num)
    {
      num = 10;
    }

    static void GetLotsOfResultsFromOneMethod(out int num, out string msg, out bool logic)
    {
      num = 20;
      msg = "changed in method";
      logic = true;
    }

    static void PromptForExit()
    {
      Console.Write(Environment.NewLine + "Program complete! Hit enter to exit...");
      Console.ReadLine();
    }
  }
}