C# Records: Immutability & Equality Explained

Imagine data that multiplies effortlessly, stays pure, and knows its twins by heart. C# records aren't just syntax sugar—they're your ticket to safer, faster .NET apps.

C# records diagram showing immutability, with expressions, and value equality comparison to classes

Key Takeaways

  • C# records deliver value-based equality and immutability out-of-the-box, perfect for DTOs and APIs.
  • Use 'with' expressions for non-mutating copies; record structs for stack-allocated performance.
  • Swap classes for records in data scenarios— but keep classes for identity-driven entities.

What if your code could birth perfect copies of data objects with one lazy tweak—leaving the original blissfully unchanged?

That’s the magic of C# records, folks. Introduced in C# 9 and turbocharged in 10, these bad boys look like classes but pack value-based equality and baked-in immutability. They’re not hype. They’re a shift—like swapping rickety horse carts for steam engines in the Victorian code forge.

Records hit the scene as reference types designed for data that doesn’t lie. Here’s the one-liner that changes everything:

public record Person(string Name, int Age);

Boom. Constructor? Check. Init-only properties? Yup. ToString override? Done. Equals and GetHashCode based on values, not memory addresses? Absolutely. Deconstruction? With expressions? All in.

Look, classes fool you. Two PersonClass instances with identical Name and Age? They’re strangers in memory, p1 == p2 spits false. Records? Same values, same soul—true every time. It’s the killer feature devs sleep on.

And immutability? Properties lock down post-construction. Try person.Name = “Bob”; compiler laughs. No sneaky mutations mid-flight.

But here’s the genius—with expressions. Want a clone with Age bumped to 31?

var updated = original with { Age = 31 };

Original pristine. Copy minted. Shallow, efficient. Like photocopying a blueprint, tweaking one line, without smudging the master.

Why Do Classes Suck at Equality (And Records Don’t)?

Classes cling to reference equality like a bad ex—same object ID or bust. Records? They judge by content. Alice aged 30 meets another Alice 30? Wedding bells.

With a class, two objects are equal only if they are the same object in memory. With a record, two objects are equal if their values are the same.

That’s straight from the docs, and it’s gospel. Punch it into tests: Assert.Equal(expected, actual) just works for value shapes. No custom overrides needed.

Deconstruction’s effortless too. var (name, age) = person; Unpacks like a gift. Positional, automatic. Records whisper, “I’m data, treat me as such.”

My hot take? Records echo functional programming’s immutable ethos—think Clojure lists or Haskell’s purity—but in C#’s comfy OOP home. Microsoft’s sly nod: .NET’s evolving into a FP-OOP hybrid powerhouse. Prediction: By 2025, 70% of ASP.NET APIs run on records. Classes for behavior? Sure. Data? Records rule.

Record Structs: Stack Wizards for High-Performance Data

C# 10 drops record struct. Value type. No heap, no GC hiccups. For tiny, hot-path value objects.

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

a == b? True on values. With expressions? Check. Stack-allocated bliss for coordinates, metrics, keys.

Compare:

Feature Record Record Struct Class
Type Reference Value Reference
Equality Value Value Reference
Heap Alloc Yes No Yes
Best For DTOs Small values Entities

Structs scream speed. Records? Safe sharing. Pick wisely.

Short para: Don’t sleep on this.

Records shine for DTOs zipping layers, API payloads, DDD value objects, configs. Query results that dare not mutate. Anywhere “equal values = equal things.”

When to Ditch Classes for Records (And When Not)

Green lights:

public record OrderResponse(Guid Id, string CustomerName, decimal TotalAmount, DateTime CreatedAt);

Tests? var updated = baseOrder with { TotalAmount = 200m }; Assertions via equality. Chef’s kiss.

Red flags: Domain entities with IDs—Order #123 != Order #456, even same details. Services with methods. Mutable state beasts.

Inheritance? Records only from records. public record Dog(string Name, string Breed) : Animal(Name); Equality cascades down the chain.

But corporate spin check: MS calls ‘em “immutable data types.” Truth: They’re value objects disguised as classes. Not for everything—overuse bloats heaps. My insight? Like Smalltalk’s objects-everywhere revolution, records push C# towards data-as-first-class. In AI pipelines—where immutable tensors flow— this cements .NET’s edge.

Wander a sec: Ever debug a mutated DTO ghost? Records banish it. Pass ‘em free, no copies needed. Future-proof.

Energized yet? Good.

Why Does This Matter for .NET Developers Right Now?

.NET 8 looms, records mature. Blazor, MAUI, APIs—all crave immutable bliss. Skeptics say “just use structs.” Nah—records add with, inheritance, printing. Full package.

Bold call: Records aren’t optional. They’re the platform shift to safer concurrency. Threads grab copies? No races. Async storms? Steady.

Six sentences deep: Envision microservices humming with record payloads. Equality checks fly. With tweaks spawn variants. Structs handle perf hotspots. Classes own logic. Harmony.

One sentence: Transform your code.


🧬 Related Insights

Frequently Asked Questions

What are C# records used for?

C# records are for immutable data like DTOs and API models, offering value equality and ‘with’ expressions to create copies without mutation.

When should I use record struct vs record in C#?

Use record struct for small, high-frequency value types to avoid heap allocation; stick to records for larger DTOs needing reference semantics.

Do C# records support inheritance?

Yes, but only from other records—not classes—and equality includes all base properties.

Sarah Chen
Written by

AI research editor covering LLMs, benchmarks, and the race between frontier labs. Previously at MIT CSAIL.

Frequently asked questions

What are C# records used for?
C# records are for immutable data like DTOs and API models, offering <a href="/tag/value-equality/">value equality</a> and 'with' expressions to create copies without mutation.
When should I use record struct vs record in C#?
Use record struct for small, high-frequency value types to avoid heap allocation; stick to records for larger DTOs needing reference semantics.
Do C# records support inheritance?
Yes, but only from other records—not classes—and equality includes all base properties.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by dev.to

Stay in the loop

The week's most important stories from The AI Catchup, delivered once a week.