JS
C#

JavaScript to C#

10 lessons

Progress0%
1Variables & Types2Classes & OOP3Async/Await4Array Methods → LINQ5Exception Handling6Collections7Generics8Delegates and Events9Records and Pattern Matching10File I/O
All Mirror Courses
JS
C#
Records and Pattern Matching
MirrorLesson 9 of 10
Lesson 9

Records and Pattern Matching

Immutable data types, switch expressions, and pattern matching

Introduction

In this lesson, you'll learn about records and pattern matching in C#. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
JS
From JavaScript:

In JavaScript, you're familiar with immutable data types, switch expressions, and pattern matching.

C#
In C#:

C# has its own approach to immutable data types, switch expressions, and pattern matching, which we'll explore step by step.

The C# Way

Let's see how C# handles this concept. Here's a typical example:

C#
C# Example
// record: immutable by default, value equality, ToString built-in
public record Point(double X, double Y);
public record Circle(double Radius) : Shape;
public record Rect(double W, double H)   : Shape;

// 'with' expression — non-destructive mutation
var p1 = new Point(1, 2);
var p2 = p1 with { X = 5 };  // Point(5, 2)

// switch expression with pattern matching
string Describe(Shape shape) => shape switch
{
    Circle { Radius: var r }        => $"circle r={r}",
    Rect   { W: var w, H: var h }   => $"rect {w}x{h}",
    _                               => "unknown"
};

// Property pattern
bool IsLarge(Shape s) => s is Circle { Radius: > 10 };

// Positional pattern (deconstruct)
bool IsOrigin(Point p) => p is (0, 0);

// List pattern (C# 11)
bool StartsWithOne(int[] arr) => arr is [1, ..];

Comparing to JavaScript

Here's how you might have written similar code in JavaScript:

JS
JavaScript (What you know)
// Immutable-ish objects via Object.freeze
const point = Object.freeze({ x: 1, y: 2 });

// Spread for "copy with change"
const moved = { ...point, x: 5 };

// switch statement
function describe(shape) {
  switch (shape.type) {
    case "circle":  return `circle r=${shape.radius}`;
    case "rect":    return `rect ${shape.w}x${shape.h}`;
    default:        return "unknown";
  }
}

// Destructuring
const { x, y } = point;
Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

record declares an immutable type with auto-generated constructor, Equals, ToString

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

'with' expression creates a modified copy without mutation

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

switch expression (=>) returns a value; each arm ends with ,

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

Property patterns match on property values inside { }

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

_ is the discard/default arm; .. is the slice pattern for collections

Step-by-Step Breakdown

1. Record Types

Records are concise immutable data classes. They auto-generate constructor, Equals (structural), GetHashCode, and ToString.

JS
JavaScript
const p = Object.freeze({ x: 1, y: 2 });
C#
C#
record Point(double X, double Y);
var p = new Point(1, 2);
Console.WriteLine(p); // Point { X = 1, Y = 2 }

2. with Expression

The 'with' expression creates a shallow copy of a record with some properties changed — perfect for functional update patterns.

JS
JavaScript
const updated = { ...obj, x: 5 };
C#
C#
var updated = obj with { X = 5 };

3. Switch Expressions

Switch expressions are exhaustive and return a value. Use _ for the default arm.

JS
JavaScript
switch(x) { case 1: return "one"; default: return "other"; }
C#
C#
string result = x switch {
    1 => "one",
    2 => "two",
    _ => "other"
};

4. Pattern Matching

Property patterns let you match and deconstruct in one expression. Combine with when for guards.

JS
JavaScript
if (shape.type === "circle" && shape.radius > 10) ...
C#
C#
if (shape is Circle { Radius: > 10 } c)
    Console.WriteLine($"Large circle: {c.Radius}");
Rule of Thumb
Prefer switch expressions over switch statements for pattern-heavy code — they're exhaustive and return values.

Common Mistakes

When coming from JavaScript, developers often make these mistakes:

  • record declares an immutable type with auto-generated constructor, Equals, ToString
  • 'with' expression creates a modified copy without mutation
  • switch expression (=>) returns a value; each arm ends with ,
Common Pitfall
Don't assume C# works exactly like JavaScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • record = immutable value type with structural equality and auto-generated ToString
  • 'with' expression creates a modified copy — the functional update pattern
  • switch expression returns a value; each arm uses => not : and ends with ,
  • Property patterns ({ Prop: pattern }) match and deconstruct in one step
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your JavaScript code in C# to practice these concepts.
PreviousNext