It is important to understand two fundamental points about the type system in the .NET Framework:
It supports the principle of inheritance. Types can derive from other types, called base types. The derived type inherits (with some restrictions) the methods, properties, and other members of the base type. The base type can in turn derive from some other type, in which case the derived type inherits the members of both base types in its inheritance hierarchy. All types, including built-in numeric types such as System..::.Int32 (C# keyword: int), derive ultimately from a single base type, which is System..::.Object (C# keyword: object). This unified type hierarchy is called the Common Type System (CTS). For more information about inheritance in C#, see Inheritance (C# Programming Guide).
Each type in the CTS is defined as either a value type or a reference type. This includes all custom types in the .NET Framework class library and also your own user-defined types. Types that you define by using the struct keyword are value types; all the built-in numeric types are structs. Types that you define by using the class keyword are reference types. Reference types and value types have different compile-time rules, and different run-time behavior.
The following illustration shows the relationship between value types and reference types in the CTS.
Value types and reference types in the CTS
.png)
Note: |
|---|
You can see that the most commonly used types are all organized in the System namespace. However, the namespace in which a type is contained has no relation to whether it is a value type or reference type. |
Value Types
Value types derive from System..::.ValueType, which derives from System..::.Object. Types that derive from System..::.ValueType have special behavior in the CLR. Value type variables directly contain their values, which means that the memory is allocated inline in whatever context the variable is declared. There is no separate heap allocation or garbage collection overhead for value-type variables.
There are two categories of value types: struct and enum.
The built-in numeric types are structs, and they have properties and methods that you can access:
// Static method on type Byte.
byte b = Byte.MaxValue;
But you declare and assign values to them as if they were simple non-aggregate types:
byte num = 0xA;
int i = 5;
char c = 'Z';
Value types are sealed, which means, for example, that you cannot derive a type from System..::.Int32, and you cannot define a struct to inherit from any user-defined class or struct because a struct can only inherit from System..::.ValueType. However, a struct can implement one or more interfaces. You can cast a struct type to an interface type; this causes a boxing operation to wrap the struct inside a reference type object on the managed heap. Boxing operations occur when you pass a value type to a method that takes a System..::.Object as an input parameter. For more information, see Boxing and Unboxing (C# Programming Guide).
You use the struct keyword to create your own custom value types. Typically, a struct is used as a container for a small set of related variables, as shown in the following example:
public struct CoOrds
{
public int x, y;
public CoOrds(int p1, int p2)
{
x = p1;
y = p2;
}
}
For more information about structs, see Structs (C# Programming Guide). For more information about value types in the .NET Framework, see Value Types in the Common Type System.
The other category of value types is enum. An enum defines a set of named integral constants. For example, the System.IO..::.FileMode enumeration in the .NET Framework class library contains a set of named constant integers that specify how a file should be opened. It is defined as shown in the following example:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
The Create constant has a value of 2. However, the name is much more meaningful for humans reading the source code, and for that reason it is better to use enumerations instead of constant literal numbers.
All enums inherit from System..::.Enum, which inherits from System..::.ValueType. All the rules that apply to structs also apply to enums. For more information about enums, see Enumeration Types (C# Programming Guide).
Reference Types
A type that is defined as a class, delegate, array, or interface is a reference type. At run time, when you declare a variable of a reference type, the variable contains the value null until you explicitly create an instance of the object by using the new operator, or assign it an object that has been created elsewhere by using new, as shown in the following example:
MyClass mc = new MyClass();
MyClass mc2 = mc;
An interface must be initialized together with a class object that implements it. If MyClass implements IMyInterface, you create an instance of IMyInterface as shown in the following example:
IMyInterface iface = new MyClass();
When the object is created, the memory is allocated on the managed heap, and the variable holds only a reference to the location of the object. Types on the managed heap require overhead both when they are allocated and when they are reclaimed by the automatic memory management functionality of the CLR, which is known as garbage collection. However, garbage collection is also highly optimized, and in most scenarios it does not create a performance issue. For more information about garbage collection, see Automatic Memory Management.
All arrays are reference types, even if their elements are value types. Arrays implicitly derive from the System..::.Array class, but you declare and use them with the simplified syntax that is provided by C#, as shown in the following example:
// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };
// Access an instance property of System.Array.
int len = nums.Length;
Reference types fully support inheritance. When you create a class, you can inherit from any other interface or class that is not defined as sealed, and other classes can inherit from your class and override your virtual methods. For more information about how to create your own classes, see Classes and Structs (C# Programming Guide). For more information about inheritance and virtual methods, see Inheritance (C# Programming Guide).