Choosing Between Classes, Structs, and Records in C#
By FoxLearn 1/2/2025 8:45:29 AM 134
Classes, structs, and records are fundamental C# types with distinct features.
- Classes are reference types in C# that support key object-oriented concepts, including encapsulation, inheritance, and polymorphism.
- Structs are value types that offer better performance but have limitations in size and mutability.
- Records, introduced in C# 9, combine the advantages of both classes and structs, offering immutability by default.
Using classes in C#
In C#, a class is a reference type, meaning a variable of a class type holds a reference to an object. Multiple references can point to the same object, and modifying the object through one reference will affect the others. A class's members such as fields, properties, methods, and events define the object's behavior and state.
The syntax for defining a class in C# is as follows:
<modifiers> class <className> { // Data members and member functions }
Here's an example of a class definition for a Product
:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } public string Category { get; set; } }
You can instantiate the Product
class and set its properties like this:
var product = new Product() { ProductId = 101, Name = "Laptop", Price = 1200.50, Category = "Electronics" };
In this example, the Product
class represents an item with properties like ProductId
, Name
, Price
, and Category
. An instance of the Product
class is created with specific values for these properties.
Using structs in C#
In C#, a struct is a value type, meaning a variable of a struct type holds an actual instance of the type, not a reference. They can also be made immutable by using the readonly
modifier. When structs are used as method parameters or assigned to another variable, they are passed by value, meaning a copy of the struct is made.
Unlike classes, structs do not support object-oriented principles like abstraction, encapsulation, inheritance, or polymorphism.
The syntax for defining a struct in C#:
<modifiers> struct <structName> { // Data members and methods }
Here’s an example of a Rectangle
struct:
struct Rectangle { public int width; public int height; // Method to calculate area public int GetArea() { return width * height; } }
You can create and initialize an instance of the Rectangle
struct and calculate its area like this:
Rectangle rect = new Rectangle(); rect.width = 10; rect.height = 5; int area = rect.GetArea(); Console.WriteLine($"Area of the rectangle: {area}");
In this example, the Rectangle
struct defines a simple geometric shape with width and height, and a method to calculate its area. The struct is instantiated, and its properties are set to calculate and print the area.
Using records in C#
Record types in C# 9 were introduced to represent immutable values. While they are reference types like classes, they behave similarly to structs by providing value-based equality by default.
Some key features of record types include:
- Immutability: You can set properties only during initialization using the
init
accessor. - With expressions: To create a new record with modified data.
- Limited inheritance support: Records support inheritance, but do not support all object-oriented features like abstraction and polymorphism.
Records are commonly used for representing data transfer objects (DTOs) and other structures that require immutability and equality semantics.
Here’s a typical class definition for a Book
:
public class Book { public string Title { get; set; } public string Author { get; set; } public int YearPublished { get; set; } }
The same Book
type can be represented as a record type like this:
public record Book(string Title, string Author, int YearPublished);
You can create an instance of the Book
record type as follows:
var book = new Book("C# Programming", "John Doe", 2021);
With a record, the Book
object is immutable, meaning you can’t change the Title
, Author
, or YearPublished
properties once it has been initialized. You can also create a modified copy of the record using the with
expression:
var updatedBook = book with { YearPublished = 2022 };
This example shows how record types simplify the creation of immutable data structures, and the value-based equality makes it easy to compare objects based on their data rather than their references.
Using inheritance in record types in C#
In C#, record types can inherit from other record types, but they cannot inherit from classes. This allows for creating a hierarchy of record types where a child record type can extend a parent record type and inherit its properties. However, records cannot extend classes, as they are designed to maintain value-based equality and immutability.
Consider the following example where a Vehicle
record is extended by a Car
record type:
public record Vehicle(string Make, string Model, int Year) { } public record Car(string Color, int Doors, string Make, string Model, int Year) : Vehicle(Make, Model, Year) { public string Color { get; set; } public int Doors { get; set; } }
You can create an instance of the Car
record like this:
var myCar = new Car("Red", 4, "Toyota", "Corolla", 2022);
In this example:
- The
Car
record inherits from theVehicle
record, allowing it to reuse theMake
,Model
, andYear
properties. - The
Car
record adds its own properties such asColor
andDoors
. - This showcases how inheritance in record types allows for extending shared properties while keeping immutability and value-based equality semantics.
Classes vs structs vs records in C#
Use classes in C# to model complex data structures and implement object-oriented principles like abstraction, encapsulation, inheritance, and polymorphism. However, it's important to consider their performance drawbacks, as classes are reference types and may incur additional memory overhead and garbage collection costs.
Structs, as value types, are more memory-efficient than reference types (classes and records) because they avoid the overhead of garbage collection. They are ideal for creating small composite data types with few members (typically less than 16 bytes) that require value semantics. Using a struct helps avoid garbage collection costs and related overhead.
Record types bridge the gap between reference and value types, promoting clean and readable code. They are ideal for data-focused tasks, such as creating data transfer objects, API responses, configuration objects, immutable models, and value objects in domain-driven design. Record types excel in pattern matching and are ideal for complex data structures. They are immutable by default, supporting functional programming by preventing modifications after creation.
When choosing between classes, structs, and records in C#, consider their usage, features, equality, immutability, and performance. Classes are best for objects with behavior and complex logic, structs are ideal for lightweight values with minimal behavior, and records are perfect for immutable data structures with simple equality rules.
- How to fix 'Failure sending mail' in C#
- How to Parse a Comma-Separated String from App.config in C#
- How to convert a dictionary to a list in C#
- How to retrieve the Executable Path in C#
- How to validate an IP address in C#
- How to retrieve the Downloads Directory Path in C#
- C# Tutorial
- Dictionary with multiple values per key in C#