C# - Data Types
C# - Data Types
Section titled “C# - Data Types”In C#, a data type specifies the type of data that a variable can hold, such as a number, text, or a true/false value. As a statically-typed and type-safe language, C# requires you to declare a variable’s type before you use it. This helps catch errors at compile-time, leading to more robust and reliable software.
C# data types are broadly categorized into two main kinds: Value Types and Reference Types.
1. Value Types
Section titled “1. Value Types”Value types directly contain their data and are typically stored on the stack, which is a fast region of memory for short-lived data. When you assign a value-type variable to another, a copy of the value is made.
Common value types include:
- Integral types:
int,long,byte,short(for whole numbers). - Floating-point types:
double,float,decimal(for numbers with fractions). - Other types:
bool(true/false),char(a single character),enum(a set of named constants), andstruct(a user-defined value type).
Numeric Types
Section titled “Numeric Types”| Type | Size (Bytes) | Common Use Case |
|---|---|---|
| int | 4 | General purpose integer (e.g., age, count) |
| long | 8 | Very large integers (e.g., database IDs) |
| double | 8 | General purpose floating-point (e.g., scientific calculations) |
| decimal | 16 | High-precision financial calculations (e.g., money) |
int age = 30;double pi = 3.14159;decimal price = 19.99m; // 'm' suffix for decimallong population = 8_000_000_000L; // 'L' suffix for long, '_' for readabilityBoolean and Character Types
Section titled “Boolean and Character Types”bool isComplete = true;char grade = 'A';2. Reference Types
Section titled “2. Reference Types”Reference types do not store the actual data directly. Instead, they store a reference (or a memory address) to the data, which is located on the heap, a memory region for longer-lived objects. When you assign a reference-type variable to another, only the reference is copied, not the object itself. Both variables then point to the same object.
Common reference types include:
string: A sequence of characters.object: The ultimate base class for all types in .NET.- Arrays: A collection of elements of the same type.
- Classes, Interfaces, Delegates, Records: User-defined reference types.
// s1 holds a reference to an object on the heap.string s1 = "Hello";
// s2 now holds a copy of the reference, pointing to the SAME object.string s2 = s1;
// Note: string is a special case; it's immutable. Operations that seem// to modify a string actually create a new string object.
// A standard class examplevar person1 = new Person { Name = "John" };var person2 = person1; // Both refer to the same Person objectperson2.Name = "Jane";
Console.WriteLine(person1.Name); // Output: Jane3. The var Keyword: Implicit Typing
Section titled “3. The var Keyword: Implicit Typing”The var keyword lets the compiler infer the type of a variable from the value it is initialized with. This can make code more readable by reducing redundancy.
Important: var does not mean the variable is dynamically typed (like in JavaScript). The type is still determined at compile-time and cannot be changed later. It’s just syntactic sugar.
var message = "This is a string"; // Compiler infers stringvar count = 100; // Compiler infers intvar prices = new List<decimal>(); // Compiler infers List<decimal>4. Nullable Types
Section titled “4. Nullable Types”By default, reference types can be null, while value types cannot. C# provides features to handle nullability safely.
- Nullable Value Types: To make a value type nullable, append a
?to its type declaration. Example:int? age = null;. - Nullable Reference Types (NRTs): A modern C# feature (enabled by default in new projects) that helps you avoid the dreaded
NullReferenceException. The compiler warns you if you try to use a reference type that might benullwithout checking it first.
// NRTs enabled in project file: <Nullable>enable</Nullable>
string nonNullableName = "World"; // Cannot be nullstring? nullableName = null; // Can be null
Console.WriteLine($"Hello, {nonNullableName.ToUpper()}"); // Safe
// Compiler warning: nullableName may be null here.// Console.WriteLine($"Hello, {nullableName.ToUpper()}");
// Proper check:if (nullableName != null){ Console.WriteLine($"Hello, {nullableName.ToUpper()}");}5. Type Conversion
Section titled “5. Type Conversion”Converting data from one type to another is a common task.
- Implicit Conversion (Safe): Happens automatically when no data can be lost.
inttodoubleis safe. - Explicit Conversion (Casting): Required when data might be lost. You must use the cast operator
(type).doubletointis explicit because the fractional part is lost. - Helper Methods (Safest): The
Convertclass andTryParsemethods are the best way to handle conversions from external sources like user input or files.
// 1. Implicitint myInt = 10;double myDouble = myInt; // Safe, no data loss
// 2. Explicit (Casting)double anotherDouble = 9.78;int anotherInt = (int)anotherDouble; // Explicitly cast, result is 9
// 3. Helper Methods (Best Practice)string userInput = "123";if (int.TryParse(userInput, out int parsedValue)){ Console.WriteLine($"Successfully parsed: {parsedValue}");}else{ Console.WriteLine("Invalid number format.");}6. Advanced: Pointer Types
Section titled “6. Advanced: Pointer Types”Pointer types are a feature inherited from C/C++. They store the memory address of a variable. This is an advanced feature and is rarely used in typical C# application code. It’s mainly for performance-critical scenarios or for interoperating with unmanaged code (like C++ libraries).
Warning: Code using pointers must be marked with the unsafe keyword and may require a specific compiler setting (<AllowUnsafeBlocks>true</AllowUnsafeBlocks> in the .csproj file). Use with extreme caution.
public class UnsafeExample{ public static unsafe void Main() { int number = 20;
// The '&' operator gets the memory address of the variable int* pointerToNumber = &number;
Console.WriteLine($"Value of number: {number}"); Console.WriteLine($"Address of number: {(long)pointerToNumber}"); Console.WriteLine($"Value via pointer: {*pointerToNumber}");
// Modify the value using the pointer *pointerToNumber = 30; Console.WriteLine($"New value of number: {number}"); }}