Please navigate to the bottom of the page for Table of Contents

Sunday, May 22, 2011

Pass by value versus reference in C#

Functions in C# may have a sequence of parameters. There are 3 ways a parameter can be passed into a function:

  • Pass by value (default way – no prefix; assumes that the variable value is set before the function is called)
  • Pass by reference (prefix with keyword ref; assumes that the variable value is set before the function is called)
  • Pass by reference (prefix with keyword out; assumes that the variable value will be set by the calling function)

When a variable is passed by reference using ref modifier, both the variables point to the same memory location. Note, that 2 variables do get created; but they point to the same memory location pointing at the same object. To use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword.

Similarly, the out keyword causes arguments to be passed by reference. This is like the ref keyword, except that ref requires that the variable be initialized before it is passed. To use an out parameter, both the method definition and the calling method must explicitly use the out keyword.

Let's now review this simple series of functions to review the important differences between pass by value, pass by reference using ref and pass by reference using out. Also note the differences in behavior for value types and objects when passed to functions.

using System;

namespace SimpleCodingQuestions
{
class Program
{
/// <summary>
/// When a value type is passed by value
/// changes to the passed parameter
/// does not affect the original value
/// </summary>
/// <param name="input"></param>
public static void PassByValue(int input)
{
input = input + 10;
Console.WriteLine("In function PassByValue: input = {0}", input);
}

/// <summary>
/// when a value is passed by reference
/// any changes to the parameter in the
/// function affects the parameter
/// outside the function
/// </summary>
/// <param name="input"></param>
public static void PassByRef(ref int input)
{
input = input + 50;
Console.WriteLine("In function PassByRef: input = {0}", input);
}

/// <summary>
/// when a value is passed by reference
/// any changes to the parameter in the
/// function affects the parameter
/// outside the function
/// </summary>
/// <param name="input"></param>
public static void PassByOut(out int input)
{
// when out is used, the parameter needs to be
// initialized before it can be used
input = 75;
Console.WriteLine("In function PassByOut: input = {0}", input);
}

/// <summary>
/// When objects are passed, they are implicitly passed
/// as reference
/// any changes made in the function
/// will reflect outside also
/// </summary>
/// <param name="emp"></param>
public static void PassingObjectsByValue(Employee emp)
{
emp.Name = "PassingObjectsByValue";
Console.WriteLine("In function PassingObjectsByValue: employee name = {0}", emp.Name);
}

/// <summary>
/// When objects are passed, they are implicitly passed
/// as reference
/// any changes made in the function
/// will reflect outside also
/// THE REF KEYWORD IS NOT NEEDED HERE
/// IT IS EXACTLY THE SAME AS ABOVE FUNCTION
/// </summary>
/// <param name="emp"></param>
public static void PassingObjectsByRef(ref Employee emp)
{
emp.Name = "PassingObjectsByRef";
Console.WriteLine("In function PassingObjectsByRef: employee name = {0}", emp.Name);
}

static void Main(string[] args)
{
// demo of passing value types vy value, ref and out
int input = 5;
Console.WriteLine("In Main: input = {0}", input);
PassByValue(input);
Console.WriteLine("In Main after calling PassByValue: input = {0}\n", input);
PassByRef(ref input);
Console.WriteLine("In Main after calling PassByRef: input = {0}\n", input);
PassByOut(out input);
Console.WriteLine("In Main after calling PassByOut: input = {0}\n", input);

// demo of passing objects by value and ref
Employee emp = new Employee() { Id = 5, Name = "Test" };
Console.WriteLine("In Main: Employee Name = {0}", emp.Name);
PassingObjectsByValue(emp);
Console.WriteLine("In Main after calling PassingObjectsByValue: Employee Name = {0}\n", emp.Name);
PassingObjectsByRef(ref emp);
Console.WriteLine("In Main after calling PassingObjectsByRef: Employee Name = {0}\n", emp.Name);
}
}

/// <summary>
/// Dummy class for demonstrating passing objects
/// </summary>
class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
}


I know this is a long program but note the functions, their invocations in the main and the corresponding output below.


image 


As you can see, PassByValue() function's changes to the local variable did not affect the main value, but passing by ref and passing by out did change the value in the main() function. Also, note that objects are inherently passed by reference.

5 comments:

  1. This depicts something of a misleading relationship between passing an object and passing an object with ref. They aren't really the same. They happen to be the same in this case, because you never reassign emp, but should you change your code to do that, it suddenly does matter.

    Ref also sends up a giant red flag to the callers that says "Hey, I may or may not swap out this parameter you're about to give me and replace it with a totally different object, potentially invalidating any instance comparisons you wanted to do."

    ReplyDelete
  2. Passing a reference type w/ the "ref" keyword is *not* exactly the same as passing without it in one particular scenario.

    For eg:
    static void Main(string[] args)
    {
    StringBuilder sb=new StringBuilder();
    sb.Append("test");

    RefTest(ref sb);

    Console.WriteLine(sb.ToString());
    }


    static void RefTest(ref StringBuilder sb)
    {
    sb = new StringBuilder();
    sb.Append("newtest");
    }
    In the example above, "newtest" is indeed written to the console.However, if you remove the "ref" from the method def and the call, then "test" is returned instead.

    ReplyDelete
  3. I believe the way it works is that when you pass a reference type (like an object) it passes a POINTER to that object BY VALUE. You'll essentially have 2 pointers to the same memory (one that lives in the scope of the function and one that lives in the calling code) so if you change that memory, you'll see it from both pointers. If you set the parameter to null, it won't affect the calling code's variable. The pointer has been passed by value.

    On the other hand, when you use the ref keyword, you're passing the POINTER BY REFERENCE. You can still change the memory it points to and you'll see it in both places in code as well. However if you set the variable to null, it will change the variable in the calling code to null as well. You've passed the pointer by reference so it's the same pointer.

    ReplyDelete