Class vs Struct
Introduction to Classes
A class in C# is a reference type, meaning that variables of this type store references (memory addresses) to objects rather than the actual object itself. It means that when a class is instantiated, memory is allocated to store the object's data.
When a class is declared:
- By default, its value is null until an instance is explicitly created using the
newkeyword. - Alternatively, an object from another compatible type can be assigned.
When you define a class, you are essentially defining a new data type that can have properties (data) and methods (functions) associated with it.
Key Points
-
Reference Type
- A class is a reference type, meaning that when an instance of a class (an object) is created, the variable holding it does not contain the actual data. Instead, it holds a reference (or memory address) to where the data is stored in memory.
-
Memory Allocation
- When you instantiate a class using the
newkeyword, memory is allocated on the heap to store the object's data. The variable used to access this object holds a reference to this memory location.
- When you instantiate a class using the
Example
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// Creating an instance of the Person class
Person person1 = new Person();
person1.Name = "Alice";
person1.Age = 30;
// Creating another reference to the same object
Person person2 = person1;
// Modifying the object through the second reference
person2.Age = 35;
// Both person1 and person2 refer to the same object
Console.WriteLine(person1.Age); // Output: 35
Console.WriteLine(person2.Age); // Output: 35
Explanation:
Personis a class.person1andperson2are variables that hold references to the samePersonobject.- Changing the
Ageproperty throughperson2also affectsperson1because both variables refer to the same object in memory.
This behavior differs from value types (like int, float, struct), where the variable holds the actual data, and copying the variable creates a new copy of the data.
Understanding this distinction is crucial for managing memory and avoiding unintended side effects in your programs.
Metaphor:
A class is like an architectural blueprint for a house. The blueprint itself does not take up space, but once a house is built (instantiated), it occupies a physical location.
Practical Applications
Classes are beneficial when dealing with:
-
Large objects that require complex operations.
-
Shared state, where multiple parts of an application need to interact with the same data.
- Example: in games, a
Playerclass might store the player's health, position, and other attributes.
- Example: in games, a
-
Inheritance, which allows code reuse and extension of existing functionality.
- Example: a
Vehicleclass might be inherited byCar,Truck, andMotorcycleclasses, which share common properties and methods.
- Example: a
Example Use Case:
In an e-commerce system, a Customer class might be used to represent shoppers, containing attributes like Name, Email, and methods such as PlaceOrder().
Key Considerations
- Classes support inheritance, making them suitable for scenarios where multiple related entities share behavior.
- As a reference type, a class variable stores a memory address rather than the actual object, meaning improper handling can lead to null reference exceptions if not instantiated properly.
Introduction to Structs
A struct in C# is a value type, meaning that variables of this type hold the actual data rather than a reference to an object in memory.
Key Points
-
Value Type
- Unlike classes, structs store their actual data directly in the variable, not a reference.
- When a struct is assigned to another variable, a copy of the data is made, meaning the two variables operate independently.
-
Memory Allocation
- Structs are allocated on the stack, which provides faster access compared to heap allocation.
- They are suitable for small, short-lived objects that do not require complex behavior.
Example
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
// Creating an instance of the struct
Point point1 = new Point();
point1.X = 10;
point1.Y = 20;
// Copying struct data to another variable
Point point2 = point1;
// Modifying the copied struct
point2.X = 50;
// Original struct remains unchanged
Console.WriteLine(point1.X); // Output: 10
Console.WriteLine(point2.X); // Output: 50
Explanation:
Pointis a struct.point1andpoint2are separate copies, meaning changes made to one do not affect the other.- Structs behave similarly to primitive types like
intandfloatin terms of memory management.
When to Use Structs
Structs are ideal for:
- Small data models, such as coordinates, colors, and dimensions.
- Performance-critical applications, where avoiding heap allocation is beneficial.
- Immutable objects, where the state should not change after creation.
- Simple entities, which do not require inheritance or complex behaviors.
Example Use Case:
In a graphics application, a Color struct might be used to represent RGB values efficiently without unnecessary overhead.
Key Considerations
- No inheritance: Structs cannot inherit from other structs or classes.
- Efficient memory use: They can lead to better performance in cases with frequent object creation and disposal.
- Copy behavior: Since structs are copied when assigned, careful consideration is needed to avoid unintended data duplication.
Differences Between Classes and Structs
Understanding the fundamental differences between classes and structs in C# is essential for making the right design decisions when developing applications.
| Feature | Class (Reference Type) | Struct (Value Type) |
|---|---|---|
| Memory Allocation | Allocated on the heap, accessed via reference | Allocated on the stack, holds actual data |
| Copy Behavior | Copies reference (points to the same object) | Creates a new copy of the entire object |
| Performance | Slower due to heap allocation and garbage collection | Faster for small data due to stack allocation |
| Inheritance | Supports inheritance ✅ | Does not support inheritance ❌ |
| Mutability | Mutable by default | Can be made immutable easily |
| Usage | Best for complex objects with shared state | Best for small, lightweight objects |
Mutability in Classes and Structs
Mutability refers to whether or not the state (data) of an object can be changed after it is created.
Key Points:
-
Mutable by Default (Classes)
-
In C#, class instances are mutable by default, meaning their properties and fields can be modified after creation without any special effort.
-
Example:
public class Person { public string Name { get; set; } public int Age { get; set; } } Person person1 = new Person { Name = "Alice", Age = 30 }; Console.WriteLine(person1.Age); // Output: 30 person1.Age = 35; // The object's state is modified Console.WriteLine(person1.Age); // Output: 35 -
Since classes are reference types, multiple references to the same object can result in unintended side effects, as all references will reflect any changes made.
-
-
Can Be Made Immutable Easily (Structs)
- Structs in C# can be made immutable by using the
readonlymodifier for fields and properties, ensuring their state cannot be changed after creation. - Example:
public struct Point { public readonly int X { get; } public readonly int Y { get; } public Point(int x, int y) { X = x; Y = y; } } Point p1 = new Point(10, 20); p1.X = 30; // ❌ Compilation error: cannot modify readonly property - Immutable structs are useful for ensuring data integrity and thread safety.
- Structs in C# can be made immutable by using the
Why Does Mutability Matter?
- Performance:
Immutable objects can be optimized by the compiler and runtime for better performance. - Thread Safety:
Immutable objects do not require synchronization, making them safer in multithreaded environments. - Data Integrity:
Ensuring that values do not change unexpectedly improves code reliability and debugging.
Common Pitfalls When Choosing
-
Overusing Classes for Small Data:
If a class is used for small, frequently created objects, it can lead to excessive garbage collection, slowing down the application. -
Using Large Structs:
Structs should remain small; using large structs can lead to excessive memory copying and performance degradation. -
Ignoring Value vs Reference Semantics:
Accidentally treating structs like classes can result in unexpected behavior, such as failing to update values correctly.