DEV Community

Cover image for Why Records in C# Are Game-Changing
Spyros Ponaris
Spyros Ponaris

Posted on

Why Records in C# Are Game-Changing

🌟 Why Records in C# Are Great

If you’ve been working with C# lately, you’ve probably encountered records, a feature introduced in C# 9 that many developers have come to love.

They’re elegant, powerful, and remove a lot of boilerplate when modeling data.

But what actually makes them great? Here’s why records aren’t just nice β€” they’re game-changing.


🧠 What Are Records?

At their core, records are reference types designed for immutable data modeling. Think of them as classes optimized for holding data β€” with extra features built in.

You can define a record in two main ways:

βœ… 1. Positional Syntax (Concise and Clean)

public record Person(string FirstName, string LastName);
Enter fullscreen mode Exit fullscreen mode

This one-liner gives you:

  • Constructor
  • Equals(), GetHashCode(), and ToString() overrides
  • init-only properties (immutable)
  • Deconstruction support

✨ Ouaaa...

In order to implement all that in a regular class, you'd need to write a ton of code.
Records do it all for you, automatically and cleanly.

βœ… 2. Classic Syntax (More Flexible)

public record Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

Need mutability? You can use set instead of init, but that sacrifices immutability and thread safety.

πŸ’‘ What is init?

init is a special accessor introduced in C# 9 that allows properties to be set only during initialization β€” making them immutable afterward.

var person = new Person { FirstName = "Alice", LastName = "Green" };
Enter fullscreen mode Exit fullscreen mode

// person.FirstName = "Bob"; ❌ Not allowed

βœ… Cleaner than readonly fields
βœ… Safer than public setters
βœ… Perfect for records, DTOs, and configuration objects

πŸ‘‰ Microsoft Docs – Init-only Setters

🎯 Why Records Are Actually Great
βœ… 1. Value-Based Equality β€” Finally Done Right
With classes, equality checks compare references.
With records, it’s based on content:

var a = new Person("Alice", "Smith");
var b = new Person("Alice", "Smith");
Console.WriteLine(a == b); // True βœ…
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Microsoft Docs – Records

βœ… 2. Immutability by Default β€” and Thread-Safe by Nature
Records are immutable by default, leading to safer, more predictable code:

var p1 = new Person("John", "Doe");
var p2 = p1 with { FirstName = "Jane" }; // Creates a copy
Enter fullscreen mode Exit fullscreen mode

Since their state can't be changed after creation, records are thread-safe for read operations β€” perfect for:

  • Parallel tasks
  • Background services
  • Blazor state containers
  • Event sourcing

πŸ‘‰ Immutability in C#

βœ… 3. Concise and Readable Code
Records eliminate boilerplate. You define what matters β€” the compiler handles the rest.

public record Invoice(string Id, DateTime Date, decimal Amount);

Clean. Lightweight. Clear.

βœ… 4. Deconstruction and Pattern Matching
Records play perfectly with modern C# features:

var (first, last) = new Person("Ana", "Lopez");

if (p1 is { FirstName: "John" })
{
    Console.WriteLine("Hello John!");
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Pattern Matching in C#

βœ… 5. Functional Programming Friendly
Records align beautifully with functional principles:

  • No side effects
  • Easy transformations
  • Clear data flow

var updatedInvoice = invoice with { Amount = invoice.Amount + 10 };
πŸ‘‰ Welcome to C# 9 – Records

βœ… 6. Supports Inheritance, Structs, and More

You’re not locked into one pattern:

  • record struct β€” value-type records
  • readonly record struct β€” fully immutable
  • Abstract/sealed record inheritance supported

πŸ‘‰ What's New in C# 10 – Record Structs

🧱 Records and the Value Object Pattern
In Domain-Driven Design (DDD), Value Objects are:

  • Immutable
  • Without identity
  • Compared by value

βœ… Records = Ideal for Value Objects

public record Email(string Address)
{
    public Email
    {
        if (!Address.Contains("@"))
            throw new ArgumentException("Invalid email");
    }
}
Enter fullscreen mode Exit fullscreen mode
public record Money(decimal Amount, string Currency);
Enter fullscreen mode Exit fullscreen mode

You get clean, reusable types with built-in equality and optional validation.

public record Product
{
    public Guid Id { get; init; }
    public string Name { get; init; }
    public Money Price { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Martin Fowler – Value Object

🚫 When NOT to Use Records
Avoid records if:

  • You need fully mutable entities (e.g., for EF Core)
  • You rely on reference identity
  • You have complex inheritance hierarchies

πŸ›  Using Records as Value Objects in EF Core

Records are great for modeling value objects in Domain-Driven Design β€” and EF Core supports them perfectly with OwnsOne.

Let’s say you have a simple Money value object:

public record Money(decimal Amount, string Currency);
Enter fullscreen mode Exit fullscreen mode

You can embed it in an entity like this:

public class Product
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Money Price { get; set; } // βœ… Value Object
}
Enter fullscreen mode Exit fullscreen mode

Then, configure it in your DbContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>(builder =>
    {
        builder.HasKey(p => p.Id);

        builder.OwnsOne(p => p.Price, money =>
        {
            money.Property(m => m.Amount)
                .HasColumnName("PriceAmount")
                .HasPrecision(18, 2);

            money.Property(m => m.Currency)
                .HasColumnName("PriceCurrency")
                .HasMaxLength(3);
        });
    });
}
Enter fullscreen mode Exit fullscreen mode

This makes your ___domain model clean, immutable, and EF-friendly β€” all thanks to record + OwnsOne.

πŸ”§ Real-World Use Cases

βœ… DTOs for APIs
βœ… Immutable configs
βœ… Logging and audit trails
βœ… Domain Value Objects
βœ… State models in Blazor, Redux, Fluxor
βœ… Event-driven architecture

🧠 Final Thoughts

C# records are more than just syntactic sugar β€” they promote a mindset of immutability, clarity, and value-based design.

They:

  • Reduce bugs
  • Simplify data models
  • Improve thread safety
  • Save tons of development time

Whether you're building APIs, desktop apps, or microservices, records make your C# experience cleaner and smarter.

Once you go record, you’ll never want to class again.

πŸ“Ž Want to Know More About Records?

If you're curious to dive deeper into C# records, check out my other article:

πŸ‘‰ Exploring Records in C#

It includes practical tips, comparisons with classes, and real-world scenarios.

πŸ“š References

Microsoft Docs – Records

Init-only Setters in C# 9

Immutability

Pattern Matching

Dev Blog – Welcome to C# 9

Martin Fowler – Value Object

Top comments (2)

Collapse
 
auyeungdavid_2847435260 profile image
David Au Yeung

This works well in the ever-changing world of APIs 😁

Collapse
 
stevsharp profile image
Spyros Ponaris

Exactly! Especially when dealing with DTOs in integrations , records help enforce consistency while being lightweight to update.