C# Tutorial Lesson 4: Variable Types (1): Reference Types and Value Types

C# is a type-safe language. Variables are declared as being of a particular type, and each variable is constrained to hold only values of its declared type.

Variables can hold either value types or reference types, or they can be pointers. This lesson covers the first two options; pointers are discussed in lesson 5.

Here's a quick recap of the difference between value types and reference types.

- where a variable v contains a value type, it directly contains an object with some value. No other variable v' can directly contain the object contained by v (although v' might contain an object with the same value).

- where a variable v contains a reference type, what it directly contains is something which refers to an object. Another variable v' can contain a reference to the same object refered to by v.

Value Types

It is possible in C# to define your own value types by declaring enumerations (lesson 7) or structs (lesson 11). These user-defined types are mostly treated in exactly the same way as C#'s predefined value types, although compilers are optimised for the latter. The following table lists, and gives information about, the predefined value types. Because in C# all of the apparently fundamental value types are in fact built up from the (actually fundamental) object type, the list also indicates which System types in the .Net framework correspond to these pre-defined types.

C# Type.Net Framework (System) typeSigned?Bytes OccupiedPossible Values
sbyteSystem.SbyteYes1-128 to 127
shortSystem.Int16Yes2-32768 to 32767
intSystem.Int32Yes4-2147483648 to 2147483647
longSystem.Int64Yes8-9223372036854775808 to 9223372036854775807
byteSystem.ByteNo10 to 255
ushortSystem.Uint16No20 to 65535
uintSystem.UInt32No40 to 4294967295
ulongSystem.Uint64No80 to 18446744073709551615
floatSystem.SingleYes4Approximately ±1.5 x 10-45 to ±3.4 x 1038 with 7 significant figures
doubleSystem.DoubleYes8Approximately ±5.0 x 10-324 to ±1.7 x 10308 with 15 or 16 significant figures
decimalSystem.DecimalYes12Approximately ±1.0 x 10-28 to ±7.9 x 1028 with 28 or 29 significant figures
charSystem.CharN/A2Any Unicode character (16 bit)
boolSystem.BooleanN/A1 / 2true or false

In the following lines of code, two variables are declared and set with integer values.

int x = 10;
int y = x;
y = 20; // after this statement x holds value 10 and y holds value 20

Reference Types

The pre-defined reference types are object and string, where object - as we have mentioned above - is the ultimate base class of all other types. New reference types can be defined using 'class', 'interface', and 'delegate' declarations (covered in lesson 12).

Reference types actually hold the value of a memory address occupied by the object they reference. Consider the following piece of code, in which two variables are given a reference to the same object (for the sake of the example, this object is taken to contain the numeric property 'myValue').

object x = new object();
x.myValue = 10;
object y = x;
y.myValue = 20; // after this statement both x.myValue and y.myValue equal 20

This code illustrates how changing a property of an object using a particular reference to it is reflected in all other references to it. Note, however, that although strings are reference types, they work rather more like value types. When one string is set to the value of another, eg

string s1 = "hello";
string s2 = s1;

Then s2 does at this point reference the same string object as s1. However, when the value of s1 is changed, for instance with

s1 = "goodbye";

what happens is that a new string object is created for s1 to point to. Hence, following this piece of code, s1 equals "goodbye", whereas s2 still equals "hello".

The reason for this behaviour is that string objects are 'immutable'. That is, the properties of these objects can't themselves change. So in order to change what a string variable references, a new string object must be created.

Escape Sequences and Verbatim Strings

When declaring a string variable, certain characters can't, for various reasons, be included in the usual way. C# supports two different solutions to this problem.

The first approach is to use 'escape sequences'. For example, suppose that we want to set variable a to the value:

"Hello World
How are you"

We could declare this using the following command, which contains escape sequences for the quotation marks and the line break.

string a = "\"Hello World\nHow are you\"";

The following table gives a list of the escape sequences for the characters that can be escaped in this way:

CharacterEscape Sequence
'\'
"\"
\\\
Alert\a
Backspace\b
Form feed\f
New Line\n
Carriage Return\r
Horizontal Tab\t
Vertical Tab\v
A unicode character specified by its number e.g. \u200 \u
A unicode character specified by its hexidecimal code e.g. \xc8\x
null\0 (zero)

The second approach is to use 'verbatim string' literals. These are defined by enclosing the required string in the characters @" and ". To illustrate this, to set the variable 'path' to the following value:

C:\My Documents\

we could either escape the back-slash characters

string path = "C:\\My Documents\\"

or use a verbatim string thus:

string path = @"C:\MyDocuments\"

Usefully, strings written using the verbatim string syntax can span multiple lines, and whitespace is preserved. The only character that needs escaping is the double-quote character, the escape sequence for which is two double-quotes together. For instance, suppose that you want to set the variable 'text' to the following value:

the word "big" contains three letters.

Using the verbatim string syntax, the command would look like this:

string text = @"the word ""big"" contains three letters."

Boxing

C# allows you convert any value type to a corresponding reference type, and to convert the resultant 'boxed' type back again. The following piece of code demonstrates boxing. When the second line executes, an object is initiated as the value of 'box', and the value held by i is copied across to this object. It is interesting to note that the runtime type of box is returned as the boxed value type; the 'is' operator thus returns the type of box below as 'int'.

int i = 123;
object box = i;
if (box is int)
{Console.Write("Box contains an int");} // this line is printed

Link Building Information