Value Out and Ref Parameters in C#

C# specifically defines 4 kinds of parameters; they are:

  1. Value parameters
  2. Output parameters (out keyword)
  3. Reference parameters (ref keyword)
  4. Parameter arrays (param keyword)

In this article, I am going to discuss the first 3 kinds of parameters.

Value parameters

These are the most commonly used parameters.

Program Listing 1.1

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace OutRefParamSample

{

    class Program

    {

        static void Main(string[] args)

        {

            int a = 20;

            Foo(a);

        }

        static void Foo(int x)

        {

            Console.WriteLine("x value : {0}", x);

        }

    }

}


In the above code, the Foo(…) method is declared with one integer parameter x which is a value parameter.

Value parameters are plain parameters that we have all used without knowing what C# specifically really calls them. Value parameters are input-only parameters, meaning the parameter reads its value from the caller and any changes to the parameter in the called method will not affect its value in the caller. For e.g. in program listing 1.2, I've assigned a value to x in the method Foo(…) but it won't affect variable "a" in the caller.

Program Listing 1.2
 

static void Foo(int x)

{

Console.WriteLine("x value : {0}", x);

x = 30; // will not change variable 'a' in the Main(...) method

}


Output parameters

Output parameters are marked with the keyword "out". Have a look at the program listing 1.3:

Program Listing 1.3
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace OutRefParamSample

{

    class Program

    {

        static void Main(string[] args)

        {

            int a = 20;

            Console.WriteLine("a value before Foo was called {0}", a);

            // Foo must be called with a variable.

            // literals and expressions are not allowed.

            // For e.g. Foo(out a + b) or Foo(out 200) aren't allowed.

            Foo(out a);

            Console.WriteLine("a value after Foo was called {0}", a);

        }

        static void Foo(out int x)

        {

            // because x is an out parameter, it's value is not read from the caller.

            // hence, we must give it a value before using it in the WriteLine(...)

            // method. Failing to do so will violate C# rule that prohibits uninitialized

            // local variables from being used.

            x = 100;

            Console.WriteLine("x value inside Foo : {0}", x);

            x = 30; // will change variable 'a' in the method Main(...)

        }

    }

}


In the above listing, pay particular attention to the comments. A few important points about output parameters are listed below.

  1. Output parameters are marked with the keyword "out".

  2. The keyword "out" must be used both in the caller and in the method being called.

  3. Output parameters are output-only parameters, meaning they don't read any value from the caller. It is for this reason, that attempts to use them before providing a value will result in an error.

  4. Output parameters must be given a value before the program's control reaches the caller. Comment out all 3 lines of code in the Foo(…) method and see what happens.

  5. Changing an output parameter's value in the called method will change the value of the variable in the caller.

  6. Unlike value parameters, it is not allowed to use literals or expressions when calling the method.

  7. Output parameters are useful when you want to return more than one value to the caller. Listing 1.4 demonstrates this.

Program Listing 1.4
 

static int Divide(int x, int y, out int remainder)

{

remainder = x % y;

       return x / y;

}


Reference parameters

Reference parameters are marked with the keyword "ref". Some of the important points about reference parameters are given below.

  1. They are bi-directional in that they read values from the caller and any change to the value in the called method will be reflected in the caller.

  2. Unlike output parameters, it is not mandatory to provide a value to the reference parameter in the method being called. If a value is provided then it is reflected in the caller. The C# compiler won't complain even if no new value is given to the parameter.

  3. Like output parameters, literals and expressions are not permitted as arguments. A variable marked with "ref" must be used by the caller.

Program listing 1.5 demonstrates a canonical example to swap values of 2 variables using reference parameters.

Program Listing 1.5
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace OutRefParamSample

{

    class Program

    {

        static void Main(string[] args)

        {

            int x = 50;

            int y = 200;

            Console.WriteLine("Before swap : x = {0} y = {1}", x, y);

            Swap(ref x, ref y);

            Console.WriteLine("After swap : x = {0} y = {1}", x, y);

        }

        static void Swap(ref int a, ref int b)

        {

            int t;

            t = a;

            a = b;

            b = t;

        }

    }

}


Reference types and parameters

So far, all programs in the article have dealt with integer parameters which are value types. Things get a bit confusing, not too confusing but a little, when reference types are involved. I will outline a few points and present some code (Listing 1.6) that hopefully clears up the confusion.

  1. When a reference type is used as a value parameter, it is the reference that goes into the method as a value. Any changes made to the state of the object that the reference is pointing to will be visible to any part of the program, including the caller, referencing the same object.

  2. Using a reference type as a value parameter won't have any bearing on the caller if the parameter is changed to point to a new object by creating a new instance of the reference type in question.

  3. Contrary to point #2, if a reference type is used as an out or ref parameter then changing the reference in the called method to point to a new object will change the reference in the caller to point to the new object.

Pay attention to the comments in Listing 1.6 and connect them with the preceding points.

Program Listing 1.6
 

using System;

 

namespace OutRefParamSample

{

    class Student

    {

        public string Name { get; set; }

    }

    class Program

    {

        static void Main(string[] args)

        {

            Student aStudent = new Student { Name = "John" };

            Student anotherStudent = new Student { Name = "Jennifer" };

            Console.WriteLine("Before Foo called.");

            Console.WriteLine("Names: {0} - {1}", aStudent.Name, anotherStudent.Name);

            Foo(aStudent, ref anotherStudent);

            Console.WriteLine("After Foo called.");

            Console.WriteLine("Names: {0} - {1}", aStudent.Name, anotherStudent.Name);

        }

        static void Foo(Student s, ref Student s1)

        {

            // point #1: for value parameter object state changes are reflected in the

            // caller.

            s.Name = "Peter";

            // point #2: for value parameters changing the reference to a new object

            // won't have any impact on the caller

            s = new Student { Name = "Steve" }; // Name is still Peter in the caller

            // point #3: for reference parameters, changing the reference to a new object

            // will also change the caller's reference to the new object.

            // anotherStudent gets Bill Gates as his name

            s1 = new Student { Name = "Bill Gates" };

        }

    }

}


Similar Articles