VodiSoft
In modern C# and .NET development, class and record serve different architectural purposes. This article explains the core differences between them, including reference vs value equality, mutable vs immutable design, DTO and Entity usage, CQRS patterns, and real-world software architecture examples. You’ll learn when to use class, when to prefer record, and how professional .NET applications structure their models efficiently.

C# Class vs Record: Differences, Use Cases, and Real-World Project Examples

In this article, we will explore the differences between class and record in C# with practical software development examples.


1. What is a Class?

A class is the fundamental reference type in C# used for creating objects.

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

var user1 = new User { Id = 1, Name = "Emre" };
var user2 = user1;

user2.Name = "Ahmet";

Console.WriteLine(user1.Name); // Ahmet


Both variables reference the same object in memory.

---

## 2. What is a Record?

`record` was introduced in C# 9 and is designed primarily for data-centric models.

```csharp
public record UserDto(int Id, string Name);

var user1 = new UserDto(1, "Emre");
var user2 = new UserDto(1, "Emre");

Console.WriteLine(user1 == user2); // True

Records provide value-based equality by default.


3. The Core Difference Between Class and Record

For a class:

Is this the same object?

For a record:

Do these objects contain the same data?

Example:

public class ProductClass
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public record ProductRecord(int Id, string Name);
var c1 = new ProductClass { Id = 1, Name = "Laptop" };
var c2 = new ProductClass { Id = 1, Name = "Laptop" };

var r1 = new ProductRecord(1, "Laptop");
var r2 = new ProductRecord(1, "Laptop");

Console.WriteLine(c1 == c2); // False
Console.WriteLine(r1 == r2); // True

4. Why Classes Are Better for Mutable Objects

Classes are typically used for objects whose state changes over time.

public class Basket
{
    public int Id { get; set; }
    public List<string> Products { get; set; } = new();

    public void AddProduct(string product)
    {
        Products.Add(product);
    }
}

5. Why Records Are Better for Immutable Data

Records are ideal for DTOs, requests, responses, and value objects.

public record CreateUserRequest(string Name, string Email);

6. Using the with Expression

One of the most powerful features of records is the with expression.

public record UserDto(int Id, string Name, string Email);

var user1 = new UserDto(1, "Emre", "[email protected]");

var user2 = user1 with
{
    Email = "[email protected]"
};

This allows immutable-style object updates.


7. ToString Differences

Class:

Console.WriteLine(user);
// Namespace.UserClass

Record:

Console.WriteLine(user);
// UserRecord { Id = 1, Name = Emre }

Records provide cleaner debug output by default.


8. Built-in Deconstruction Support

Records automatically support deconstruction.

public record UserDto(int Id, string Name);

var user = new UserDto(1, "Emre");

var (id, name) = user;

9. Why Entities Usually Use Classes

Entities are identity-based and behavior-oriented.

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    public decimal Price { get; set; }

    public void ChangePrice(decimal newPrice)
    {
        if (newPrice <= 0)
            throw new ArgumentException();

        Price = newPrice;
    }
}

Entities contain business logic and mutable state.


10. Why DTOs Are Great Candidates for Records

public record ProductResponse(
    int Id,
    string Name,
    decimal Price
);

DTOs mainly carry data and rarely contain behavior.


11. Records for Value Objects

public record Money(decimal Amount, string Currency);
var money1 = new Money(100, "TRY");
var money2 = new Money(100, "TRY");

Console.WriteLine(money1 == money2); // True

Value objects are naturally value-based.


12. Why Services and Repositories Should Be Classes

public class PaymentService
{
    public void Pay(decimal amount)
    {
        // payment logic
    }
}

Services contain behavior, not just data.


13. Records in CQRS and MediatR

public record CreateProductCommand(
    string Name,
    decimal Price
);
public record GetProductByIdQuery(int Id);

14. record class vs record struct

public record Person(string Name);

public record struct Point(int X, int Y);

public readonly record struct Coordinate(int X, int Y);
  • record class → reference type
  • record struct → value type

15. Real-World Project Example

Entity:

public class Order
{
    public int Id { get; private set; }
    public int CustomerId { get; private set; }
    public decimal TotalAmount { get; private set; }
    public string Status { get; private set; }

    public void Approve()
    {
        if (Status != "Pending")
            throw new InvalidOperationException();

        Status = "Approved";
    }
}

DTO:

public record OrderResponse(
    int Id,
    int CustomerId,
    decimal TotalAmount,
    string Status
);

Command:

public record CreateOrderCommand(
    int CustomerId,
    List<int> ProductIds
);

Value Object:

public record OrderPrice(decimal Amount, string Currency);

16. Quick Decision Table

Scenario Preferred Type
Entity class
Service class
Repository class
Mutable Object class
DTO record
API Request record
API Response record
Value Object record
Event Model record
Command/Query record

17. Final Rule of Thumb

Use class when:

  • Identity matters
  • The object contains behavior
  • The object changes over time

Use record when:

  • The object only carries data
  • You prefer immutable design
  • Value-based equality is needed

Practical mindset:

Does this object perform actions? → class
Does this object only carry data? → record
Does identity matter? → class
Do values matter more? → record

In professional .NET projects, the most common approach is:

  • class for entities, services, repositories, and behavior-heavy models
  • record for DTOs, requests, responses, commands, queries, and value objects

18. Quick Decision Table

Scenario Preferred Type
Entity class
Service class
Repository class
Controller class
Manager class
Mutable Object class
DTO record
API Request/Response record
Value Object record
Event Model record
Command/Query Model record
Config/Options Model class or record
EF Core Entity usually class
Log/Event Payload record

19. Real-World Project Example

Entity:

public class Order
{
    public int Id { get; private set; }
    public int CustomerId { get; private set; }
    public decimal TotalAmount { get; private set; }
    public string Status { get; private set; }

    public void Approve()
    {
        if (Status != "Pending")
            throw new InvalidOperationException("Only pending orders can be approved.");

        Status = "Approved";
    }
}

DTO:

public record OrderResponse(
    int Id,
    int CustomerId,
    decimal TotalAmount,
    string Status
);

Command:

public record CreateOrderCommand(
    int CustomerId,
    List<int> ProductIds
);

Value Object:

public record OrderPrice(decimal Amount, string Currency);

The distinction here is clear:

  • Order is an entity → class
  • OrderResponse carries data → record
  • CreateOrderCommand carries data → record
  • OrderPrice is a value object → record

20. The Most Practical Rule

Use class when:

  • Identity matters
  • The object contains behavior
  • The object changes over time

Use record when:

  • The object only carries data
  • You prefer immutable design
  • You need value-based equality

A practical mindset:

Does this object perform actions? → class
Does this object only carry data? → record
Does identity matter? → class
Do values matter more? → record
Does the object change over time? → class
Should the object remain immutable after creation? → record

In professional .NET projects, the most common approach is:

  • Use class for entities, services, repositories, and behavior-heavy models
  • Use record for DTOs, requests, responses, commands, queries, and value objects