PY
C#

Python to C#

10 lessons

Progress0%
1Variables & Types2Classes & OOP3Collections & LINQ4Async/Await5Exception Handling6File I/O7Generics8Delegates and Events9Records and Pattern Matching10Interfaces
All Mirror Courses
PY
C#
Records and Pattern Matching
MirrorLesson 9 of 10
Lesson 9

Records and Pattern Matching

C# record types vs Python dataclasses, switch expressions vs match

Introduction

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

Mirror Card
PY
From Python:

In Python, you're familiar with c# record types vs python dataclasses, switch expressions vs match.

C#
In C#:

C# has its own approach to c# record types vs python dataclasses, switch expressions vs match, 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, value equality, auto ToString
public record Point(double X, double Y);

// 'with' expression — like dataclasses.replace()
var p1 = new Point(1, 2);
var p2 = p1 with { X = 5 };  // Point { X = 5, Y = 2 }

// Mutable record (record class vs record struct)
public record class MutablePoint
{
    public double X { get; set; }
    public double Y { get; set; }
}

// Sealed hierarchy for exhaustive matching
public abstract record Shape;
public record Circle(double Radius) : Shape;
public record Rect(double W, double H)   : Shape;

// switch expression with property patterns
string Describe(Shape shape) => shape switch
{
    Circle { Radius: > 10 } c  => $"large circle r={c.Radius}",
    Circle c                   => $"circle r={c.Radius}",
    Rect r                     => $"rect {r.W}x{r.H}",
    _                          => "unknown"
};

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

Comparing to Python

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

PY
Python (What you know)
from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float

@dataclass(frozen=True)  # immutable
class ImmutablePoint:
    x: float
    y: float

# "Copy with change" via replace
from dataclasses import replace
p1 = ImmutablePoint(1, 2)
p2 = replace(p1, x=5)      # ImmutablePoint(5, 2)

# match/case (Python 3.10+)
def describe(shape):
    match shape:
        case {"type": "circle", "radius": r} if r > 10:
            return f"large circle r={r}"
        case {"type": "rect", "w": w, "h": h}:
            return f"rect {w}x{h}"
        case _:
            return "unknown"
Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C#
In C#:

record is immutable by default with value equality — like @dataclass(frozen=True)

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C#
In C#:

'with' expression creates modified copy — equivalent to dataclasses.replace()

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C#
In C#:

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

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C#
In C#:

Property patterns ({ Prop: value }) match and deconstruct in one expression

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C#
In C#:

sealed + abstract record enables exhaustive switch (compiler checks all cases)

Step-by-Step Breakdown

1. Records

A record auto-generates constructor, property accessors, Equals (structural), GetHashCode, and ToString. Perfect for data transfer objects.

PY
Python
@dataclass
class Point:
    x: float
    y: float
C#
C#
record Point(double X, double Y);
// Equivalent to:
// constructor Point(double X, double Y)
// Equals: compares X and Y values
// ToString: "Point { X = 1, Y = 2 }"

2. with Expression

The 'with' expression creates a shallow copy of a record with specified properties changed — exactly like dataclasses.replace().

PY
Python
from dataclasses import replace
p2 = replace(p1, x=5)
C#
C#
var p2 = p1 with { X = 5 };
// p1 is unchanged — records are immutable

3. Switch Expressions

Switch expressions are concise and return values. Combine with type patterns to replace long if/elif chains.

PY
Python
match value:
    case 1: return "one"
    case 2: return "two"
    case _: return "other"
C#
C#
string result = value switch {
    1 => "one",
    2 => "two",
    _ => "other"
};

4. Property and Type Patterns

Property patterns check values inside { }. Combine type pattern + property pattern for expressive matching.

PY
Python
match shape:
    case Circle(radius=r) if r > 10:
        return f"large"
C#
C#
string r = shape switch {
    Circle { Radius: > 10 } c => $"large circle r={c.Radius}",
    Circle c                  => $"small circle r={c.Radius}",
    _                         => "other"
};
Rule of Thumb
Prefer switch expressions over if/else chains for type discrimination — they're exhaustive and return values.

Common Mistakes

When coming from Python, developers often make these mistakes:

  • record is immutable by default with value equality — like @dataclass(frozen=True)
  • 'with' expression creates modified copy — equivalent to dataclasses.replace()
  • switch expression (=>) returns a value; each arm ends with , not :
Common Pitfall
Don't assume C# works exactly like Python. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • record = @dataclass(frozen=True) — immutable, value equality, auto-generated members
  • 'with' expression = dataclasses.replace() — non-destructive update
  • switch expression returns values; property patterns { Prop: value } deconstruct inline
  • sealed + record hierarchy enables exhaustive matching — compiler catches missing cases
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Python code in C# to practice these concepts.
PreviousNext