Wednesday, 24 October 2012

Value Types vs Reference Types and Parameter Passing


Value types/Reference types and Parameter Passing

To simply put what's happening in C#: unless you declare it explicitly everything is passed by value. Firstly, it is important to differentiate between value types/reference types and variables being passed as out/ref. When talking about value types, we mean that a variable of a type holds the actual value that the type represents. For example the statement "int a=2;" creates a variable of type "int" which is a value type and then assigns the value 2 to it. On the other hand reference types are variables which represent types like arrays, interfaces, classes and delegates. Variables representing these types do not actually contain that type but hold a reference to it. As an example the statement "StringBuilder sb = new StringBuilder()" creates a StringBuilder object and the assigns its reference to the variable sb. In other words sb does not hold a StringBuilder but a reference to where the object actually lives.
Another misconception about value types and reference types is the saying that "Reference types live on the heap while value types live on the stack". This statement is inaccurate. Variables position in the managed memory depends on the context in which they are declared. All local variables(variables declared inside methods) are stored on the stack. Even if the variable is a reference type, the variable itself is stored on the stack. Instance variables are located on the heap where the actual containing object is. Value types like structs live wherever the variable used to declare them resides. Meaning if the value type is a local variable or a parameter it will live on the stack, if it is a instance member it lives on the heap. Also static variables always live on the heap. 
Now that we know what a reference type and what a value type is, we can continue with different types of parameter passing. In the default form in C# all the parameters are passed by value. What does this mean for value types ? It means that a new variables is created in the callee and the value of the value type is copied to the new variable. Reference type are handled the same way. A new variable is created in the callee and the reference is copied into that new variable. Although both these variables now accommodate access to the object, notice that the they are independent from one another. For example in the previous code what is the value of obj1 after the final line is executed ?
   
public void Foo(StringBuilder obj)
{
    obj = new StringBuilder("Good Bye");
}
public void Bar()
{
    StringBuilder obj1 = new StringBuilder("Hello");
    Foo(obj1);
    Console.WriteLine(obj1);
}  

If didn't get the answer "Hello", try again and see why the answer is not "Good Bye". The reason is that like I said before the value that is being passed to the method Foo is a copy of the reference to a StringBuilder object and since the variable obj in Foo and the variable obj1 are two independent variables then the changes to obj are not noticed by obj1. If obj had made any changes to the actual object however the changes would have been seen in Bar as seen below:
   
public void Foo(StringBuilder obj)
{
    obj.Append(" There");;
}
public void Bar()
{
    StringBuilder obj1 = new StringBuilder("Hello");
    Foo(obj1);
    Console.WriteLine(obj1);
}  

The answer is "Hello There". If we had used a struct instead of the StringBuilder class however since a new value of the struct had been passed to Foo, Bar would now have seen the changes.
Now since we can already use the above mechanism to pass the references of objects by value and then manipulating the object directly, why would be want to use a ref directive?
In order to use the ref directive. One has to put it before the parameter both where the function is called and in the function definition. With this mechanism, instead of a new variable being allocated for the sake of the callee method, the same variable(slot in memory) is used for both the variable being passed and the input parameter. This means that the same code as above now does really result in "Good Bye".
   
public void Foo(StringBuilder obj)
{
    obj = new StringBuilder("Good Bye");
}
public void Bar()
{
    StringBuilder obj1 = new StringBuilder("Hello");
    Foo(obj1);
    Console.WriteLine(obj1);
}  

The out operator is the same as ref with the difference that with this operator the variable being passed does not have to be initialized since it is assumed that it is going to be assigned in the callee. Also it is required to be assigned in the callee and is considered uninitialized in the callee's context.

No comments:

Post a Comment