- 1 A First C# Program
- 2 Namespaces
- 3 Alternative Forms of the Main() Function
- 4 Making a Statement
- 5 Opening a Text File for Reading and Writing
- 6 Formatting Output
- 7 The string Type
- 8 Local Objects
- 9 Value and Reference Types
- 10 The C# Array
- 11 The new Expression
- 12 Garbage Collection
- 13 Dynamic Arrays: The ArrayList Collection Class
- 14 The Unified Type System
- 15 Jagged Arrays
- 16 The Hashtable Container
- 17 Exception Handling
- 18 A Basic Language Handbook for C#
1.9 Value and Reference Types
Types in C# are categorized as either value or reference types. The behavior when copying or modifying objects of these types is very different.
An object of a value type stores its associated data directly within itself. Any changes to that data does not affect any other object. For example, the predefined arithmetic types, such as int and double, are value types. When we write
double pi = 3.14159;
the value 3.14159 is directly stored within pi.
When we initialize or assign one value type with another, the data contained in the one is copied to the second. The two objects remain independent.
For example, when we write
double shortPi = pi;
although both pi and shortPi now hold the same value, the values are distinct instances contained in independent objects. We call this a deep copy.
If we change the value stored by shortPi,
shortPi = 3.14;
the value of pi remains unchanged. Although this may seem obviousperhaps to the point of tediumthis is not what happens when we copy and modify reference types!
A reference type is separated into two parts:
A named handle that we manipulate directly.
An unnamed object of the handle's type stored on what is referred to as the managed heap. This object must be created with the new expression (see Section 1.11 for a discussion).
The handle either holds the address of an object on the heap or is set to null; that is, it currently refers to no object. When we initialize or assign one reference type to another, only the address stored within the handle is copied.
Both instances of the reference type now refer to the same object on the heap. Modifications made to the object through either instance are visible to both. We call this a shallow copy.
All class definitions are treated as reference types. For example, when we write
class Point { float x, y; // ... } Point origin;
Point is a reference type, and origin reflects reference behavior.
A struct definition allows us to introduce a user-defined value type. For example, when we write
struct Point { float x, y; // ... } Point origin;
Point is now a value type, and origin reflects value behavior. In terms of performance, a value type is generally more efficient, at least for small, heavily used objects. We look at this topic in more detail in Section 2.19.
The predefined C# array is a reference type. The discussion of the array in the next section should clarify reference type behavior.