C# Type System
Introduction to Types
The C# type system is a fundamental aspect of the language, defining how data is stored, manipulated, and interpreted. Every value in C# has a specific type, which dictates its behavior and the operations that can be performed on it.
C# supports a rich set of built-in types, also known as primitive types, as well as user-defined types that can be created using classes, structs, enums, and delegates. The Common Type System (CTS), part of the Common Language Infrastructure (CLI), provides a common framework for type definitions across different .NET languages.
Value Types vs. Reference Types
In C#, types are broadly categorized into two main groups: value types and reference types.
Value Types
Value types directly contain their data. When you assign a value type variable to another, the value is copied. Common examples include primitive types like int, float, bool, and structs.
Value Type Example
int a = 10;
int b = a; // b gets a copy of the value 10
b = 20; // Changes b, but a remains 10
In this example, both a and b are independent variables, each holding its own copy of an integer value.
Reference Types
Reference types store a reference (an address) to the actual data, which is located on the managed heap. When you assign a reference type variable to another, both variables point to the same object in memory. Examples include classes, interfaces, delegates, and strings (though strings are immutable).
Reference Type Example
List<int> list1 = new List<int>();
list1.Add(10);
List<int> list2 = list1; // list2 refers to the same object as list1
list2.Add(20);
// Now list1 also contains 10 and 20
Here, list1 and list2 both point to the same List<int> object. Modifying the list through list2 affects what is visible through list1.
Built-in Types
C# provides a comprehensive set of built-in types, which map to types defined in the .NET Framework. These can be categorized as follows:
Numeric Types
Used for representing numbers.
- Integers:
sbyte,byte,short,ushort,int,uint,long,ulong - Floating-Point:
float,double - Decimal:
decimal(for high precision financial calculations)
Boolean Type
Represents a truth value: bool (true or false).
Character Type
Represents a single Unicode character: char.
String Type
Represents a sequence of Unicode characters: string. Strings in C# are immutable.
Reference Types (Built-in)
While class and interface are keywords for user-defined types, types like object (the base type of all types) and string are also fundamental reference types.
int is an alias for System.Int32, and string is an alias for System.String.
User-Defined Types
Beyond built-in types, developers can create their own types to model complex data and behavior.
Classes
The cornerstone of object-oriented programming in C#. Classes define the blueprint for objects, encapsulating data (fields) and behavior (methods).
Structs
Similar to classes but are value types. They are typically used for small, lightweight data structures.
Enums
Define a set of named constants, making code more readable and maintainable.
Interfaces
Define contracts that classes or structs must implement, specifying a set of members that the implementing type must provide.
Delegates
Represent references to methods with a particular parameter list and return type, enabling event handling and callbacks.
Type Safety
C# is a type-safe language. This means that the compiler enforces strict rules about how types can be used, preventing operations that are not defined for a given type. This helps to catch errors at compile time rather than at runtime, leading to more robust applications.
For example, you cannot directly assign an integer to a string variable without explicit conversion, nor can you call a method on an object that is not declared to have that method.
Nullability
Value types, by default, cannot be null. However, you can declare nullable value types using the ? modifier (e.g., int?, bool?). Reference types can be null, indicating that the variable does not refer to any object.
Nullable Value Type
int? nullableInt = null; // This is valid
if (nullableInt.HasValue)
{
Console.WriteLine(nullableInt.Value);
}
else
{
Console.WriteLine("The integer is null.");
}