C# is a language where every variable that you declare must have a type. A type is defined as a set of data and the operations performed on them. It is used when declaring local variables, classes, interfaces, arrays, structures and so on. When writing applications, you can use the types already provided by C#, like int, long, and System.Environment, or you can create your own (Employee, Book, Invoice). But in either case, the types come in two different flavors: value types and reference types.
Contributed by Ayad Boudiab Rating: / 5 April 30, 2008
Value Types: A value type is either a simple type (bool,char,sbyte,byte,short,ushort,int,uint,long,ulong,float,double,decimal), astructtype, or anenumtype. A variable of a value type contains the data (as opposed to holding the address that shows where the data actually is). For example, the following declaration...
intcount = 104;
will be interpreted as:
The memory location for the variable count contains the value 104.
Assigning a value of value type to a variable of value type makes a copy of the value. Try the following code:
class Program
{
static voidMain(string[] args)
{
intcount = 104;
Console.WriteLine("Value of count is: {0}", count);
intanotherCount = count;
Console.WriteLine("Value of anotherCount is: {0}", anotherCount);
}
}
This will result in the following:
Seeing as structures and enumerations are value types as well, the same idea applies. Ponder the following example:
When it comes to passing value types between methods, it is important to note that only a copy of the value is passed into the method. Any manipulations that you do to the copy will not affect the original value. Here is an illustration:
class Program
{
static voidMain(string[] args)
{
Pointp = new Point();
p.x = 2;
p.y = 7;
Console.WriteLine("Before an attempt to move the point: {0}", p);
Move(p, 2, 2);
Console.WriteLine("After the attempt to move the point: {0}", p);
}
static voidMove(Pointp, intx_value, inty_value)
{
p.x += x_value;
p.y += y_value;
}
}
struct Point
{
public intx;
public inty;
public override stringToString()
{
return string.Format("[{0},{1}]", x, y);
}
}
The output of the program will be:
Before an attempt to move the point: [2,7]
After the attempt to move the point: [2,7]
The first question that comes to mind is: what happened to the changes I made to the structure?
Answer: p is a Point, which is a structure, which is a value type. Passing a value type to a method results in a copy being made to the value type (p in this case). The copy is passed into the method and changes are made to the copy (the copy of p will have the values [4,9], the same value you anticipated for p). By the time you hit the closing bracket for the Move function, the copy of p will be out of scope. So, you never changed p, you only changed a copy of p.
Considering that parameters are passed via a value by default, the question is: is there any way to change that behavior? Yes, that is done using therefmodifier. If you place therefmodifier in front of a value parameter, the parameter is then passed via reference instead, and obviously any changes that you make to the parameter will be maintained after the method exits. Keep in mind, though, therefmodifier needs to be placed in the method declaration and the method call. If we return to the structure example, here is how it will look:
class Program
{
static voidMain(string[] args)
{
Pointp;
p.x = 2;
p.y = 7;
Console.WriteLine("Before an attempt to move the point: {0}", p);
Move(refp, 2, 2);
Console.WriteLine("After the attempt to move the point: {0}", p);
Reference Types: A reference type, on the other hand, does not store the data itself. Instead, reference types store the address of the data. The address is also referred to as a pointer. The actual data the address refers to is stored in an area of memory called the heap. Here is an illustration:
A reference type is a class, an interface, an array type, or a delegate type. Like declaring any variable, to declare a variable of a reference type, you start with the type, followed by the variable name, such as:
Employee e;
This declaration is not sufficient, however. So fareis referencing nothing. In C# terminology, we say thatecurrently contains the valuenull. This means that we cannot access anyEmployeemember (properties, methods, etc.). To create a meaningfulEmployeeobject with which we can interact, we need thenewoperator and anEmployeeconstructor:
Thenewoperator allocates a block of memory for the object. Unlike C++, it is not the responsibility of the programmer to return this block of memory to the heap. The CLR’s garbage collector checks for objects no longer referenced and cleans them up (frees the memory).
TheEmployeeconstructor is a special method that has the same name as the class. Its purpose is to initialize the class members.
Armed with these two new facts, we can now create anEmployeeobject:
Employee e = new Employee();
Here is a graphical interpretation of the preceding statement:
Now, we can access the Employee object. We use the dot (.) operator to do so:
e.Name; //property
e.GiveRaise(0.05); //method
As stated earlier, the variable e contains the address of the Employee object (not the object itself). If we declare anotherEmployeeobject and assign it the value ofe, we will end up with another reference to the sameEmployeeobject thateis currently referencing (we will NOT have anotherEmployeeobject). After all, we are assigning references (or memory addresses).
Employee e2 = e;
The graphical representation will be as such:
So, any changes that we make to the Employee object through one reference (saye2) will be seen by the other reference (e). Note: if you are not interested in this behavior, and you want two separate Employee objects, then you should implement an interface calledClonable. This, however, is outside the scope of this article.
To see the use of classes in action, let’s take the structure example we used at the beginning of this article and replace the structure with a class. Here is how the code will look:
class Program
{
static voidMain(string[] args)
{
Pointp = new Point();
p.x = 2;
p.y = 7;
Console.WriteLine("p-> "+ p);
Pointp2 = p;
Console.WriteLine("p2-> "+ p2);
Console.WriteLine("nAfter changing the Point through p2:n");
//change the point through p2
p2.x = 12;
Console.WriteLine("p2-> "+ p2);
Console.WriteLine("p-> "+ p);
}
}
class Point
{
public intx;
public inty;
public override stringToString()
{
return string.Format("[{0},{1}]", x, y);
}
}
Output:
p-> [2,7]
p2-> [2,7]
After changing the Point through p2:
P2-> [12,7]
p-> [12,7]
Notice that sincepandp2are referencing the same object, when we changed the value ofxusingp2, the change is also reflected thoughp.
Before we wrap up our discussion of value and reference types, we have one important point about structures that we need to clarify. From our discussion of structures, we learned that we can create a structure and initialize its members using code like this:
struct Size
{
public intwidth;
public intheight;
}
Sizes;
s.width = 100;
s.height = 50;
Surprisingly enough though, C# allows you to create structures using thenewoperator:
Sizes = new Size();
But wait, structures are value types and the new operator is for reference types. What does this statement mean? Can we actually create a structure on the heap?
Well, how thenewoperator is used here is not the same as the one used with reference types. The structure is still created on the stack (not the heap). Thenewoperator here is used solely to trigger the constructor (the special method that initializes the data members). This constructor is special in that it is called a default constructor (does not take any parameter). With structures, this constructor is reserved for the runtime (which means that we cannot create our own default constructor in the structure). We can create non-default constructors though.
Conclusion: We can create two kinds of types in C#: value type (likebool,int,struct,enum) and reference type (class,interface,delegate). The value types are created on the stack and their value is popped out of the stack when the variable goes out of scope. Reference types are created on the heap and when there are no more variables referencing the object, it will eventually be cleaned up by the garbage collector. Understanding the difference between value types and reference types will help you create more efficient code and save you a lot of frustrating debugging time.