A quick guide to parameter passing possibilities in C#

These notes may remind you of various ways to pass parameters/arguments/values to/from methods/functions/subroutines in C# programs. The same sorts of mechanisms are available in other languages, and as an exercise in understanding you might like to develop a similar document for C, C++ or Pascal.

This material should already be known if you have received a thorough course in C# programming. Please work through it and make sure you understand it completely.


Parameter passing by value (or: how to feed values into a method)

Many programs use this form of parameter passing. "Formal parameters" used in a method heading are effectively "local variables" within that method: when the method is called they are initialised to the values of the expressions evaluated and used as "arguments" or "actual parameters" for that particular call.

Changes made within the method to the formal parameters are "not seen" in/by the calling routine.

     class KidsStuff {

       static int i, j, k, x, y, z;         // global variables (never used here)

       void Function(int i) {               // one parameter passed by value
         IO.Write(i * i);                   // can access the value of the actual argument(s)
       } // Function

       int Sum(int i, int j) {              // two parameters passed by value
                                            // Sum can access the values of the actual arguments
         i++;   j = 300;                    // Sum can even alter its local variables i and j
         return i + j;                      // a non-void function must "return" a value specified by
                                            // an expression after the keyword "return"
       } // Sum

       IntSet NewSet(IntSet s, int i)       // two parameters passed by value
         IntSet n = new IntSet(12, 13);     // s, i and n are entirely local to NewSet
         n.Incl(i);
         return n.Union(s);                 // a non-void function must "return" a value specified by
       } // NewSet                          // an expression after the keyword "return"

       static void Main(string[] args) {
         Function(12);                      // will write 12 * 12 that is, 144
         int s = Sum(5, 6);                 // s will be assigned 306 (work it out for yourself)
         IntSet ss = new IntSet(5, 8, 9);   // ss will be created as the set { 5, 8, 9 }
         IntSet nn = NewSet(ss, 4);         // nn will be assigned the set { 4, 5, 8, 9, 12, 13 }
       } // Main

     } // class KidsStuff


Parameter passing by reference (or: how to get values in and also out of a method)

Many programs use this form of parameter passing to transfer values both into and also out of a method when it is called and later returns. The formal parameters provide references (in low-level terms, pointers to or the addresses of) the variables used as "arguments" or "actual parameters" when the method is called.

Any changes made within the method to the formal parameters are thus effectively changes to the variables used for the arguments or formal parameters. Note the use of the key word ref in both the caller and the called parameter list.

     class KidsStuff {

       void RefSwap(ref int i, ref int j) { // two parameters passed by reference
                                            // actual parameters must be designated as variables
         int temp;                          // temp is entirely local to the RefSwap method
         temp = i; i = j; j = temp;         // changes to i and j are changes to the variables used
                                            // as actual parameters or arguments
                                            // the net result is that the exchange is made
       } // RefSwap

       void ValSwap(int i, int j) {         // two parameters passed by value
                                            // i and j are essentially local to ValSwap
                                            // initialised when the function is called
         int temp;                          // temp is entirely local to the ValSwap method
         temp = i; i = j; j = temp;         // the net result is that the intended exchange is lost
       } // ValSwap

       int RefSum(ref int i, ref int j) {   // two parameters passed by reference
                                            // can access the value of the actual arguments i and j
         return i + j;                      // a non-void function must "return" a value specified by
                                            // an expression after the keyword "return"
       } // RefSum

       int ValSum(int i, int j) {           // two parameters passed by value
                                            // i and j are essentially local to ValSum
                                            // initialised when the function is called
         return i + j;                      // a non-void function must "return" a value specified by
                                            // an expression after the keyword "return"
       } // ValSum

       static void Main(string[] args) {
         int x = 10 y = 12, z = 14;         // x, y, z are local to Main

         ValSwap(x, y);                     // x, y still have the values 10, 12
         RefSwap(ref x, ref y);             // x, y now have the values 12, 10
         ValSwap(10, 12);                   // valid call but nothing changes
         RefSwap(12, 10)                    // illegal - actual arguments must be variables

         z = ValSum(x, y);                  // z now has the value 22
         z = RefSum(ref x, ref y);          // z now has the value 22
         z = ValSum(10, 12);                // z now has the value 22
         z = RefSum(10, 12);                // illegal - actual argumentss must be variables
       } // Main

     } // class KidsStuff


Output parameter passing by reference (or: how to get values out of a method)

C# allows for what one might think of as contractual parameter passing. "Formal parameters" used in the method heading stand for references to the variables used as "arguments" or "actual parameters" when the method is called. The key word out, used both to mark formal and actual parameters, is a signal that the method being called must compute a value and assign it to the formal parameter.

Any assignments made within the method to the formal parameters marked in this way are thus effectively changes to the variables used as the arguments or formal parameters .

     class KidsStuff {

       void MaxMin(out int max, out int min, int x, int y)
                                            // two values passed in; two results passed out by reference
                                            // actual parameters for max and min must be designated as variables
         max = x > y ?  x : y;              // or if you prefer     if (x > y) max = x; else max = y;
         min = x < y ?  x : y;              // or if you prefer     if (x < y) min = x; else min = y;
       } // MaxMin

       static void Main(string[] args) {
         int i, j, bigger, smaller;         // x, y, z are local to Main
         i = 10; j = 12;
         MaxMin(out bigger, out smaller, i, j);
                                            // bigger has the value 12, smaller has the value 10
         MaxMin(out bigger, out smaller, 2 * i, j);
                                            // bigger has the value 20, smaller has the value 12
         MaxMin(out bigger, out smaller, 25, 8);
                                            // bigger has the value 25, smaller has the value 8
         MaxMin(bigger, smaller, i, j);     // invalid - key word out missing

     } // class KidsStuff


Global variables (or: how to try to avoid using parameters)

These are often expedient (useful) but should be used with great caution.

     class KidsStuff {

       static int x, y, z;
       static IntSet set1, set2;

       void Function() {                    // no parameters
         x = y;                             // can access and change class "global" variables
       } // Function

       int Sum() {                          // no parameters
                                            // can access global variables
         return x + y;                      // a non-void function must "return" a value specified by
                                            // an expression after the keyword "return"
        } // Sum

       IntSet NewSet() {                    // no parameters
                                            // can access class "global" variables
         return set1.Union(set2);           // a non-void function must "return" a value specified by
                                            // an expression after the keyword "return"
       } // NewSet

       static void Main(string[] args) {
         Function();
         int s = Sum();
         IntSet ss = NewSet();
       } // Main

     } // class KidsStuff


Side Effects (or: how to undermine the real aim of a method)

Given that the body of a method has access to the "global" variables of the class in which the method is nested. it is possible for methods that are really intended to do one thing to do other things on the sly. These are called methods with side effects, are usually frowned upon, are sometimes convenient, but should be used with great caution.

     class KidsStuff {

       static int count = 0;

       static int Fibonacci(int m) {
       // Returns the m-th Fibonacci number

         count++;    // side effect - count the number of times the method is called

         if (m == 0) return 0;
         if (m == 1) return 1;
         return Fibonacci(m - 1) + Fibonacci(m - 2);
       } // Fibonacci

       static void Main(string[] args) {
         IO,WriteLine(Fibonacci(12));       // use the method properly
         IO.WriteLine(count);               // rely on the side effect to give hidden information
       } // Main

     } // class KidsStuff