I'll be writing a few articles about Object Oriented Programming (OOP), but before I do that, there's something I need to explain. In this article, I will be discussing the difference between value types and reference types, as well as the use of the ref keyword.
Let us first create a new console application using SharpDevelop (or Visual Studio, if you prefer). In this application, we declare a struct that represents a monster in a game (if you don't remember structs, look back at "C# Basics: Snake Game in ASCII Art (Part 1)"):
struct Monster
{
public string name;
public int health;
};
This struct goes under the namespace, at the same level as class Program. We can now make instances of Monster in the Main() method:
Monster wolf = new Monster();
wolf.name = "Rex";
wolf.health = 100;
Monster steve = new Monster();
steve.name = "Steve Ballmer";
steve.health = 10;
Next, we define a couple of methods that work on a monster and modify its attributes:
public static void RenameMonster(Monster monster, String newName)
{
monster.name = newName;
}
public static void HurtMonster(Monster monster, int damage)
{
monster.health -= damage;
}
In Main(), we can test this:
Console.WriteLine("Monster's name before: {0}", wolf.name);
RenameMonster(wolf, "Wolverine");
Console.WriteLine("Monster's name after: {0}", wolf.name);
Console.WriteLine("Monster's health before: {0}", wolf.health);
HurtMonster(wolf, 5);
Console.WriteLine("Monster's health after: {0}", wolf.health);
Console.ReadLine();
The output, however, is not quite what we were expecting:
If we change the Monster from a struct to a class and re-run the program, on the other hand, it works correctly:
So, what exactly is happening here? A struct is an example of a value type. When you pass it as a parameter to a function, a copy of it is actually passed to the function. If you modify it within that function, you are actually modifying the copy. When the function returns, the struct still has its old values.
A class, on the other hand, is a reference type. When you pass an instance of a class (i.e. an object) as a parameter to a function, the object itself is passed. You can make changes to the class's members and they are retained even after the function returns.
Most simple data types (such as integers, floats, etc), as well as structs and enumerations, are value types. Classes and strings are reference types, although a string behaves pretty much like a value type when passed to a function (see an explanation on Stack Overflow, if you're interested).
[Sidenote for purists: in C#, functions are actually called "methods". I consider the term "method" to be more of an OOP thing, and what we're doing here isn't quite OOP.]
It is, however, possible to pass a value type by reference. Let's say you have this function:
public static void Increment(int x)
{
x++;
}
If we try to call this function on a variable as it is, we're back to square one:
However, we can use the ref keyword to mark parameters that we want to pass by reference:
public static void Increment(ref int x)
{
x++;
}
...and do the same in the calling code:
Increment(ref x);
This gives the result we want:
Wonderful. The bottom line is that structs and classes are very similar, but there are a few subtle differences (such as value type vs reference type, stack allocation vs heap allocation, etc) that can affect the way your code works. The differences are particularly clear if you know what pointers are. We will be using classes a lot for Object Oriented Programming (OOP), so it's important to know a little bit about them.
In the next article, we will dive straight into OOP! :)
Thanks. This explained the difference between reference and value better than the examples I was using.
ReplyDelete