Intermediate · Inheritance, interfaces and records · topic 21 of 43 · 30 min
Records and pattern matching
Explanation
Why records?
Much of a business app is data moving around: an order request, a search result, a message on a queue. For such data you want: easy creation, no accidental changes, and "same data means equal". Writing that by hand in a class is long. A record (C# 9) gives it in one line.
var a = new Product("Pen", 10m);
var b = new Product("Pen", 10m);
Console.WriteLine(a); // Product { Name = Pen, Price = 10 }
Console.WriteLine(a == b); // True: same data
Console.WriteLine(a.Name); // Pen
// a.Price = 12m; // error: init-only property
public record Product(string Name, decimal Price);Value equality versus reference equality
For normal classes, == asks "is this the same object?" (reference equality). For records, == asks "do they hold the same data?" (value equality). Two different record objects with equal properties are equal. This is exactly what you want for data, and it makes tests easier to write.
with-expressions: copy with changes
var pen = new Product("Pen", 10m);
var costlier = pen with { Price = 12m };
Console.WriteLine(pen.Price); // 10 (unchanged)
Console.WriteLine(costlier.Price); // 12Deconstruction
var (name, price) = new Product("Notebook", 45m);
Console.WriteLine($"{name} costs {price}");record class and record struct
| Declaration | Kind | Notes |
|---|---|---|
| record Product(...) | Reference type (same as record class) | Properties are init-only |
| record struct Point(int X, int Y) | Value type (C# 10) | Positional properties are read-write |
| readonly record struct Point(int X, int Y) | Value type | Properties are init-only: fully immutable |
Records can also have normal bodies with extra methods and properties, and can inherit from other records. Entities whose data changes over time and which have an identity, like a database row being edited, are usually better as classes.
Pattern matching
A pattern is a test of a value's shape. You have used some already: the relational patterns >= 90 in switch expressions. C# supports many kinds, and you can combine them.
| Pattern | Example | Matches when |
|---|---|---|
| Type | x is string s | x is a string (and gives it the name s) |
| Constant | x is null, code is "UPI" | Equal to a constant |
| Relational | >= 60 | Compared to a constant |
| Logical | >= 18 and < 60, "Sat" or "Sun", not null | Combined with and, or, not |
| Property | { Age: >= 60, City: "Pune" } | Properties match their patterns |
| Positional | Point(0, 0) | Deconstructed values match |
| List (C# 11) | [1, .., 9] | Array/list items match in order |
Console.WriteLine(Concession(new Passenger("Kamala", 66, false))); // 0.4
static decimal Concession(Passenger p) => p switch
{
{ IsDisabled: true } => 0.75m,
{ Age: < 5 } => 1.0m,
{ Age: >= 60 } => 0.4m,
_ => 0m
};
public record Passenger(string Name, int Age, bool IsDisabled);object input = 42;
string result = input switch
{
int n when n < 0 => "negative number",
int n => $"number {n}",
string s => $"text of length {s.Length}",
null => "nothing",
_ => "something else"
};
Console.WriteLine(result); // number 42
int[] digits = { 1, 5, 9 };
Console.WriteLine(digits is [1, .., 9]); // TrueCommon mistakes and fixes
- Expecting records to be fully immutable: a record holding a List<T> still lets someone change the list's items. Use read-only collections for deep immutability.
- Putting a broad arm before a specific one: { Age: >= 5 } before { Age: >= 60 } means seniors never reach their arm. The compiler often warns when an arm can never match.
- Forgetting the _ arm: a non-exhaustive switch expression throws at run time for unmatched values.
- Using record struct when you wanted immutability: positional record struct properties are writable; add readonly.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Records and pattern matching · 1/7
In this lesson you will meet records, types made for data, and pattern matching.
Prefer a video by a teacher? Find free videos on “C# records with expression value equality pattern matching switch” on YouTube ↗
In real life: Classifying UPI transactions for a monthly spending summary
A personal finance app reads UPI transactions as records and uses pattern matching to place each one in a category. Because records compare by value, the app can also spot exact duplicate entries easily.
var txns = new[]
{
new Txn("Swiggy", 420m, "food"),
new Txn("IRCTC", 1850m, "travel"),
new Txn("Landlord", 15000m, "rent"),
new Txn("Swiggy", 420m, "food"),
};
foreach (var t in txns)
Console.WriteLine($"{t.Payee,-9} {Label(t)}");
Console.WriteLine(txns[0] == txns[3] ? "Possible duplicate found" : "No duplicates");
static string Label(Txn t) => t switch
{
{ Tag: "rent" } => "Fixed cost",
{ Amount: > 1000m, Tag: "travel" } => "Big travel",
{ Tag: "food", Amount: < 500m } => "Small food order",
_ => "Other"
};
public record Txn(string Payee, decimal Amount, string Tag);Practice (0/4 done)
record City(string Name, int Pin); What does Console.WriteLine(new City("Agra", 282001) == new City("Agra", 282001)); print?
var p = new Product("Pen", 10m); var q = p with { Price = 15m }; What is p.Price?
int age = 30; string s = age switch { < 18 => "minor", >= 18 and < 60 => "adult", _ => "senior" }; What is s?
Write a record Order(string Id, decimal Amount, string City, bool IsPrime). Write a method DeliveryDays(Order o) using a switch expression with property patterns: Prime orders in "Mumbai" or "Delhi" take 1 day, other Prime orders 2 days, non-Prime orders over Rs 499 take 3 days, and everything else 5 days.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Abstract classes and polymorphismInheritance, interfaces and records · same module
- Inheritance: base and derived classesInheritance, interfaces and records · same module
- toString, equals, hashCode and recordsJava Programming (Core to Spring Boot) · Classes and objects
- References, copies and equalityJavaScript Programming (language in depth) · Arrays and objects
- Decisions: if, else, switch and switch expressionsDecisions, loops and methods
- Dart 3: enums, records, patterns and sealed classesFlutter and Dart · Null safety and object-oriented Dart
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does == compare for two record objects?
Answer: Their property values
Records generate value-based equality.
What does new Product("Pen", 10m).ToString() give for record Product(string Name, decimal Price)?
Answer: Product { Name = Pen, Price = 10 }
Records print their type name and properties.
Is record struct a value type or a reference type?
Answer: Value type
record struct (C# 10) is a value type.
What does when do in a switch arm?
Answer: Adds an extra condition the arm must also satisfy
when is a case guard.
int[] a = { 2, 4, 6, 8 }; What is a is [2, .., 8]?
Answer: True
The list pattern needs 2 first, 8 last, and anything in between.
What is the default value of a reference-type field?Related
Without overriding ToString, what does printing an object of class City show?Related
Answer: Its class name (like City)
The default ToString returns the type's full name.
Which is TRUE about records?Related
Answer: They generate equals, hashCode and toString
Records are immutable data carriers with generated methods.
Which makes a deep copy that keeps Dates as Dates?Related
Answer: structuredClone(obj)
structuredClone copies every level and supports Dates, Maps and Sets.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Inheritance, interfaces and records”. The module exam comes next.
- Module 5 exam: Inheritance, interfaces and records · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Collections, generics and LINQ. 43 topics left in the course before the final exam.