Archive

Archive for June 28th, 2007

Reference & Value types - Pass by Value, Pass by Reference (.NET)

June 28th, 2007

A colleague of mine is learning C# and was asking me some questions about the difference between Reference and Value types in C#. We then went on to discuss the different semantics of passing Reference and Value types by reference or by value.

Firstly a few definitions

  • Value Type = Any structure (struct in C#), all of the primitive types eg. int, double, DateTime etc (not including string).
  • Reference Type = Anything inheriting from object, this means anything defined as a class.
  • Pointer = The location of a reference type (which is what you have when you have a Reference Type variable)
  • Mutable = If an object is mutable it’s internal state can be changed byt calling methods and/or properties, for example neither String nor DateTime are mutable since any method/property that would change state returns a new String or DateTime.

There are a number of seperate cases you need to consider, I’m going to create a Swapper class to demonstrate.

using System;public static class Swapper

{

 [STAThread]

 static void Main()

 {

  SwapperTests();

 }public static void SwapperTests()

 {

  object leftRef = "left";

  object rightRef = "right";

  int leftIntVal = 1;

  int rightIntVal = 2;

  double leftDoubleVal = 1;

  double rightDoubleVal = 2;  Swap(leftRef, rightRef); // Passing Reference Types By Value

  Swap(ref leftRef, ref rightRef); // Passing Reference Types By Reference  Swap(leftIntVal, rightIntVal); // Passing Value Types By Value

  Swap(ref leftIntVal, ref rightIntVal); // Passing Value Types By Reference  SwapGeneric(leftDoubleVal, rightDoubleVal); // Passing Value Types By Value (boxed)

  SwapGeneric(ref leftDoubleVal, ref rightDoubleVal); // Passing Value Types By Reference (boxed)

 }public static void Swap(object left, object right)

 {

  object temp = left;

  left = right;

  right = temp;

 }public static void Swap(ref object left, ref object right)

 {

  object temp = left;

  left = right;

  right = temp;

 }public static void Swap(int left, int right)

 {

  int temp = left;

  left = right;

  right = temp;

 }public static void Swap(ref int left, ref int right)

 {

  int temp = left;

  left = right;

  right = temp;

 }public static void SwapGeneric(T left, T right)

 {

  T temp = left;

  left = right;

  right = temp;

 }public static void SwapGeneric(ref T left, ref T right)

 {

  T temp = left;

  left = right;

  right = temp;

 }

}

Taking each of these in turn there are 2 things to consider

  • Will the swap work as expected?
  • If the parameters are mutable will changes be reflected outside of the method
  • Swap(leftRef, rightRef); // Passing Reference Types By Value
    1. No, when the Swap method is called the runtime creates two new reference types (left and right) and sets them to the same address as leftRef and rightRef, when the swap is carried out only the local references are changed (so left now points to “right” and right points to “left” but leftRef and rightRef are unchanged).
    2. Yes, this could catch you out! Since left and right are both valid pointers to objects any mutable objects could be changed by the method and the changes WOULD be seen in the calling method.
  • Swap(ref leftRef, ref rightRef); // Passing Reference Types By Reference
    1. Yes, this is in fact the correct way to define this method, leftRef and rightRef are passed into the Swap method so they can be changed, no copies are made!
    2. Yes, again as long as you have a pointer to a mutable object you can change it’s state.
  • Swap(leftIntVal, rightIntVal); // Passing Value Types By Value
    1. No, similarly to the object version this won’t work. Two new ints are created and passed into Swap. This means you are onhly working on copies of leftIntVal and rightIntVal so the changes are not seen outside the method
    2. No, this is different to how objects behave, with objects only the pointer is copied but with value types all the data is copied so NO changes are seen outside the method.
  • Swap(ref leftIntVal, ref rightIntVal); // Passing Value Types By Reference
    1. Yes, in this case the actual leftIntVal and leftIntVal variables are passed into the swap function so any changes are seen outside
    2. Yes, in this case the actual leftIntVal and leftIntVal variables are passed into the swap function so any changes are seen outside

In .NET v2.0 you would define the two methods as shown in SwapGeneric, these methods will correctly infer the types of variable being passed in and will behave as described above depending on what is passed in.

Or pictorially

By Ref, By Val

/Technology