[{"data":1,"prerenderedAt":114},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching":3},{"page":4,"siblings":94,"blog":111},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":6,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching.md","Pattern Matching",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,3,"\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching",[23,28,32,36,40,44,48,52,56,60,64,68,72,76,80],{"id":24,"difficulty":25,"q":26,"a":27},"pattern-matching-intro","easy","What is pattern matching in C# and when was it introduced?","**Pattern matching** is a language feature that lets you test a value against a\n*pattern* — a shape, type, or condition — and extract data from it in a single\nexpression. C# introduced basic patterns in C# 7 and added richer forms in each\nsubsequent version through C# 11.\n\n```csharp\nobject value = 42;\n\n\u002F\u002F Pre-C# 7 style (verbose):\nif (value is int)\n{\n    int n = (int)value;\n    Console.WriteLine(n * 2);\n}\n\n\u002F\u002F C# 7 — type pattern + declaration in is-expression:\nif (value is int n)\n    Console.WriteLine(n * 2); \u002F\u002F 84 — n is declared and bound in one step\n\n\u002F\u002F C# 8 — switch expression replaces switch statement:\nstring describe = value switch\n{\n    int i when i \u003C 0 => \"negative\",\n    int i when i == 0 => \"zero\",\n    int i => $\"positive: {i}\",\n    string s => $\"string: {s}\",\n    _ => \"other\"\n};\nConsole.WriteLine(describe); \u002F\u002F positive: 42\n```\n\n**Rule of thumb:** Prefer pattern matching over chains of `if (x is T) { var t = (T)x; }`\ncasts. It is safer (no invalid cast exceptions), more concise, and exhaustive in\nswitch expressions (compiler warns if cases are missing).\n",{"id":29,"difficulty":25,"q":30,"a":31},"switch-expression","How does a `switch` expression differ from a `switch` statement?","A `switch` **statement** is imperative — it executes side-effect branches. A\n`switch` **expression** (C# 8) is an expression — it *produces a value*. It is\nmore concise, discourages fall-through, and the compiler enforces exhaustiveness\n(warns if any input has no matching arm).\n\n```csharp\n\u002F\u002F switch statement (C# 1+):\nstring desc;\nswitch (statusCode)\n{\n    case 200: desc = \"OK\";           break;\n    case 404: desc = \"Not Found\";    break;\n    case 500: desc = \"Server Error\"; break;\n    default:  desc = \"Unknown\";      break;\n}\n\n\u002F\u002F switch expression (C# 8+) — same logic, 7 lines fewer:\nstring desc2 = statusCode switch\n{\n    200 => \"OK\",\n    404 => \"Not Found\",\n    500 => \"Server Error\",\n    _   => \"Unknown\",        \u002F\u002F discard pattern = default\n};\n\n\u002F\u002F switch expression returning complex types:\ndecimal tax = orderType switch\n{\n    OrderType.Standard  => subtotal * 0.20m,\n    OrderType.Reduced   => subtotal * 0.05m,\n    OrderType.ZeroRated => 0m,\n    _ => throw new ArgumentOutOfRangeException(nameof(orderType))\n};\n```\n\nThe `_` pattern is the **discard** (wildcard \u002F default). Omitting it when not all\ncases are covered produces a warning, and the expression will throw\n`SwitchExpressionException` at runtime for unhandled values.\n\n**Rule of thumb:** Prefer `switch` expressions over `switch` statements whenever\nyou are computing a value. Use `switch` statements only for side-effect branches\nthat don't produce a return value.\n",{"id":33,"difficulty":25,"q":34,"a":35},"type-pattern","What is a type pattern in C# and how does it bind a variable?","A **type pattern** tests whether a value is of a specific type and, if so, assigns\nit to a new typed variable — replacing the `is` check + cast idiom.\n\n```csharp\nobject[] items = { 42, \"hello\", 3.14, true, null };\n\nforeach (var item in items)\n{\n    \u002F\u002F Type pattern in is-expression:\n    if (item is int n)\n        Console.WriteLine($\"int: {n}\");\n    else if (item is string s)\n        Console.WriteLine($\"string: {s}\");\n\n    \u002F\u002F Type pattern in switch expression:\n    string describe = item switch\n    {\n        int i    => $\"integer {i}\",\n        string s => $\"string '{s}'\",\n        double d => $\"double {d:F2}\",\n        null     => \"null\",\n        _        => item.GetType().Name\n    };\n    Console.WriteLine(describe);\n}\n\n\u002F\u002F Inheritance works — tests the runtime type:\nShape shape = new Circle { Radius = 5 };\nif (shape is Circle c)\n    Console.WriteLine($\"Circle with radius {c.Radius}\"); \u002F\u002F matches\n```\n\nThe declared variable (`n`, `s`, `c`) is only in scope when the pattern matches —\nand it is already cast to the correct type, so no explicit cast is needed.\n\n**Rule of thumb:** Use type patterns instead of `is` + explicit cast. They are\nnull-safe (null never matches a type pattern), and the variable is guaranteed to be\nnon-null when the pattern succeeds.\n",{"id":37,"difficulty":14,"q":38,"a":39},"property-pattern","What is a property pattern and how do you nest them?","A **property pattern** (C# 8) matches an object by testing the values of its\nproperties or fields. It uses `{ PropertyName: pattern }` syntax and can be nested\narbitrarily deep.\n\n```csharp\nrecord Address(string Country, string City);\nrecord Customer(string Name, Address Address, bool IsPremium);\n\nCustomer customer = new(\"Alice\", new Address(\"UK\", \"London\"), IsPremium: true);\n\n\u002F\u002F Property pattern — match multiple properties:\nstring shipping = customer switch\n{\n    { IsPremium: true, Address.Country: \"UK\" } => \"Free UK delivery\",\n    { IsPremium: true }                         => \"Free international delivery\",\n    { Address.Country: \"UK\" }                   => \"£2.99 UK delivery\",\n    _                                            => \"£4.99 international delivery\"\n};\nConsole.WriteLine(shipping); \u002F\u002F Free UK delivery\n\n\u002F\u002F Nested property pattern (C# 10 — extended property pattern):\n\u002F\u002F { Address: { Country: \"UK\" } }  — verbose C# 8\n\u002F\u002F { Address.Country: \"UK\" }       — terse C# 10+ (dot notation)\n\n\u002F\u002F Combine with type pattern:\nobject obj = customer;\nif (obj is Customer { Name: var name, IsPremium: true })\n    Console.WriteLine($\"Premium: {name}\");\n```\n\n**Rule of thumb:** Property patterns shine in switch expressions for business-rule\ndispatching based on data shape. Keep them flat — deeply nested property patterns\nhurt readability more than they gain.\n",{"id":41,"difficulty":14,"q":42,"a":43},"positional-pattern","What is a positional pattern and how does it work with deconstruction?","A **positional pattern** (C# 8) matches values by position using a type's\n`Deconstruct` method (or a record's built-in deconstruction). It is written as\n`TypeName(pattern1, pattern2, ...)`.\n\n```csharp\n\u002F\u002F Records have built-in deconstruction:\nrecord Point(int X, int Y);\n\nPoint p = new(3, -1);\n\n\u002F\u002F Positional pattern — matches based on deconstructed positions:\nstring quadrant = p switch\n{\n    (0, 0)          => \"Origin\",\n    (> 0, > 0)      => \"Q1\",\n    (\u003C 0, > 0)      => \"Q2\",\n    (\u003C 0, \u003C 0)      => \"Q3\",\n    (> 0, \u003C 0)      => \"Q4\",\n    _               => \"On axis\"\n};\nConsole.WriteLine(quadrant); \u002F\u002F Q4\n\n\u002F\u002F Custom Deconstruct for non-record types:\nclass Rectangle\n{\n    public int Width { get; init; }\n    public int Height { get; init; }\n    public void Deconstruct(out int w, out int h) => (w, h) = (Width, Height);\n}\n\nRectangle r = new() { Width = 10, Height = 5 };\nif (r is (> 0, > 0) and (var w, var h))\n    Console.WriteLine($\"{w} x {h}\"); \u002F\u002F 10 x 5\n```\n\n**Rule of thumb:** Positional patterns are most useful with records or small tuples\nwhere the position is the natural identity. For larger objects with many properties,\nproperty patterns are more readable (explicit names).\n",{"id":45,"difficulty":14,"q":46,"a":47},"relational-logical-patterns","What are relational patterns and logical patterns in C# 9?","**Relational patterns** (C# 9) let you compare a value with `\u003C`, `\u003C=`, `>`, `>=`\ninside a pattern. **Logical patterns** (`and`, `or`, `not`) combine patterns.\n\n```csharp\nint score = 85;\n\n\u002F\u002F Relational patterns:\nstring grade = score switch\n{\n    >= 90           => \"A\",\n    >= 80 and \u003C 90  => \"B\",   \u002F\u002F logical 'and' — both must match\n    >= 70 and \u003C 80  => \"C\",\n    >= 60 and \u003C 70  => \"D\",\n    _               => \"F\"\n};\nConsole.WriteLine(grade); \u002F\u002F B\n\n\u002F\u002F Logical 'or':\nbool isWeekend = DateTime.Now.DayOfWeek switch\n{\n    DayOfWeek.Saturday or DayOfWeek.Sunday => true,\n    _ => false\n};\n\n\u002F\u002F Logical 'not' (with type patterns):\nobject obj = \"hello\";\nif (obj is not null)                  Console.WriteLine(\"not null\");\nif (obj is not int)                   Console.WriteLine(\"not an integer\");\nif (obj is not (int or double))       Console.WriteLine(\"not numeric\");\n\n\u002F\u002F Combining relational and property patterns:\nrecord Product(string Name, decimal Price);\nvar p = new Product(\"Widget\", 24.99m);\nbool isAffordable = p is { Price: >= 10 and \u003C= 50 }; \u002F\u002F true\n```\n\n**Rule of thumb:** Relational and logical patterns make range-based and exclusion\nconditions read naturally in switch expressions. Prefer `>= 80 and \u003C 90` over\na when-guard `when score >= 80 && score \u003C 90`.\n",{"id":49,"difficulty":25,"q":50,"a":51},"when-guard","What is a `when` guard in a switch expression or statement?","A `when` clause adds an extra boolean condition to a switch arm. The arm only\nmatches if the pattern matches **and** the `when` condition is true. It is useful\nfor conditions that cannot be expressed purely as a pattern.\n\n```csharp\nvar orders = new[]\n{\n    new { Id = 1, Total = 200m, IsPaid = true  },\n    new { Id = 2, Total = 50m,  IsPaid = false },\n    new { Id = 3, Total = 0m,   IsPaid = false },\n};\n\nforeach (var order in orders)\n{\n    string status = order switch\n    {\n        { IsPaid: true, Total: > 100 } => \"High-value paid\",\n        { IsPaid: true }               => \"Paid\",\n        { Total: 0 }                   => \"Empty order\",\n        var o when o.Total > 100       => \"High-value — awaiting payment\",\n        _                              => \"Pending payment\"\n    };\n    Console.WriteLine($\"Order {order.Id}: {status}\");\n}\n\u002F\u002F Order 1: High-value paid\n\u002F\u002F Order 2: Pending payment\n\u002F\u002F Order 3: Empty order\n\n\u002F\u002F switch statement with when:\nswitch (exception)\n{\n    case HttpRequestException ex when ex.StatusCode == HttpStatusCode.NotFound:\n        HandleNotFound(); break;\n    case HttpRequestException ex:\n        HandleOtherHttpError(ex); break;\n    default:\n        throw;\n}\n```\n\n**Rule of thumb:** Use `when` guards for conditions that involve method calls,\ncomplex logic, or state not captured by the matched object's properties. Keep guards\nsimple — a complex `when` clause signals the logic should move into a method.\n",{"id":53,"difficulty":14,"q":54,"a":55},"list-pattern","What is a list pattern in C# 11?","A **list pattern** (C# 11) matches an array, list, or any type implementing a\nsuitable indexer and `Count`\u002F`Length`, against a sequence of element patterns.\nThe `..` slice pattern matches zero or more elements in the middle.\n\n```csharp\nint[] empty   = [];\nint[] one     = [1];\nint[] two     = [1, 2];\nint[] several = [1, 2, 3, 4, 5];\n\n\u002F\u002F Exact match:\nConsole.WriteLine(empty   is []);        \u002F\u002F True\nConsole.WriteLine(one     is [1]);       \u002F\u002F True\nConsole.WriteLine(two     is [1, 2]);    \u002F\u002F True\n\n\u002F\u002F Length check:\nConsole.WriteLine(several is [_, _, _]); \u002F\u002F False — not exactly 3\n\n\u002F\u002F Slice:\nConsole.WriteLine(several is [1, .., 5]);       \u002F\u002F True — starts 1, ends 5\nConsole.WriteLine(several is [var first, ..]);   \u002F\u002F True — bind first element\nConsole.WriteLine(first);                         \u002F\u002F 1\n\n\u002F\u002F Destructure specific positions:\nif (several is [var head, .. var middle, var last])\n{\n    Console.WriteLine($\"head={head}, last={last}, middle count={middle.Length}\");\n    \u002F\u002F head=1, last=5, middle count=3\n}\n\n\u002F\u002F HTTP method routing example:\nstring[] segments = [\"api\", \"users\", \"42\"];\nstring route = segments switch\n{\n    [\"api\", \"users\", var id] => $\"Get user {id}\",\n    [\"api\", \"users\"]         => \"List users\",\n    _                        => \"Unknown route\"\n};\nConsole.WriteLine(route); \u002F\u002F Get user 42\n```\n\n**Rule of thumb:** List patterns simplify sequence destructuring that would otherwise\nrequire index-based checks. Most useful for small, fixed-structure arrays like\ncommand-line args, URL segments, or parsed CSV rows.\n",{"id":57,"difficulty":14,"q":58,"a":59},"deconstruction","What is deconstruction in C# and how do you implement it for custom types?","**Deconstruction** lets you unpack an object's components into discrete variables\nin a single assignment. Records generate a `Deconstruct` method automatically;\nother types implement it as a `void Deconstruct(out T1 a, out T2 b, ...)` method.\n\n```csharp\n\u002F\u002F Records — automatic Deconstruct:\nrecord Point(int X, int Y);\nvar p = new Point(3, 7);\nvar (x, y) = p;                  \u002F\u002F deconstruct\nConsole.WriteLine($\"x={x}, y={y}\"); \u002F\u002F x=3, y=7\n\n\u002F\u002F Custom class with Deconstruct:\nclass Rectangle\n{\n    public int Width  { get; init; }\n    public int Height { get; init; }\n\n    public void Deconstruct(out int width, out int height)\n        => (width, height) = (Width, Height);\n}\n\nvar rect = new Rectangle { Width = 10, Height = 5 };\nvar (w, h) = rect;              \u002F\u002F uses Deconstruct\nConsole.WriteLine($\"{w}x{h}\"); \u002F\u002F 10x5\n\n\u002F\u002F Tuples are deconstructable out of the box:\n(string name, int age) person = (\"Alice\", 30);\nvar (n, a) = person;\n\n\u002F\u002F Use _ to discard fields you don't need:\nvar (_, height) = rect;         \u002F\u002F only care about height\n\n\u002F\u002F Deconstruction in foreach:\nvar people = new[] { (\"Alice\", 30), (\"Bob\", 25) };\nforeach (var (name, age) in people)\n    Console.WriteLine($\"{name} is {age}\");\n```\n\n**Rule of thumb:** Implement `Deconstruct` on any type that has a natural set of\ncomponent parts — geometry, coordinates, key-value pairs. Prefer records over manual\nimplementation when the type is primarily data-carrying.\n",{"id":61,"difficulty":25,"q":62,"a":63},"var-pattern","What is the `var` pattern and when is it useful?","The **var pattern** always succeeds and binds the value (including `null`) to a new\nvariable, inferring its type. Unlike type patterns, it matches anything — even null.\n\n```csharp\n\u002F\u002F var pattern in switch expression — capture for use in when guard:\nobject obj = \"hello\";\nstring result = obj switch\n{\n    null              => \"null\",\n    var s when s is string str && str.Length > 3 => $\"long: {str}\",\n    var x             => $\"other: {x}\"\n};\n\n\u002F\u002F Common use — compute intermediate value for a when guard:\nint[] nums = { 1, 2, 3, 4, 5, 6 };\nvar grouped = nums.GroupBy(n => n % 2 == 0 ? \"even\" : \"odd\")\n    .Select(g => g switch\n    {\n        var group when group.Count() > 2 => $\"{group.Key}: many\",\n        var group                         => $\"{group.Key}: few\"\n    });\n\n\u002F\u002F var in deconstruction-style pattern:\nPoint p = new(5, -3);\nif (p is (var px, \u003C 0)) \u002F\u002F positional + var to capture X, relational for Y\n    Console.WriteLine($\"Below x-axis at x={px}\");\n```\n\nThe var pattern is mainly used to bind a value for use in a `when` guard, or to\ncapture a subvalue within a nested pattern that would otherwise be verbose to repeat.\n\n**Rule of thumb:** Use the `var` pattern specifically when you need to give a name\nto a matched value for use in a `when` clause. For general type-checking, prefer\na type pattern which also ensures non-null.\n",{"id":65,"difficulty":14,"q":66,"a":67},"pattern-matching-records","How does pattern matching work with C# records?","Records are ideal pattern-matching targets because they have built-in value equality,\nautomatic deconstruction, and immutable properties — all properties pattern matching\nneeds to work cleanly.\n\n```csharp\n\u002F\u002F Records support property, positional, and type patterns:\nrecord Shape;\nrecord Circle(double Radius) : Shape;\nrecord Rectangle(double Width, double Height) : Shape;\nrecord Triangle(double Base, double Height) : Shape;\n\ndouble Area(Shape shape) => shape switch\n{\n    Circle c               => Math.PI * c.Radius * c.Radius,\n    Rectangle(var w, var h) => w * h,              \u002F\u002F positional\n    Triangle { Base: var b, Height: var h } => 0.5 * b * h, \u002F\u002F property\n    _ => throw new ArgumentException($\"Unknown shape: {shape}\")\n};\n\nConsole.WriteLine(Area(new Circle(5)));          \u002F\u002F ~78.54\nConsole.WriteLine(Area(new Rectangle(4, 6)));    \u002F\u002F 24\nConsole.WriteLine(Area(new Triangle(3, 8)));     \u002F\u002F 12\n\n\u002F\u002F Nested record patterns:\nrecord Address(string Country, string City);\nrecord Order(int Id, Address ShipTo, decimal Total);\n\nvar order = new Order(1, new Address(\"UK\", \"London\"), 299.99m);\nstring delivery = order switch\n{\n    { ShipTo.Country: \"UK\", Total: > 100 }  => \"Free UK delivery\",\n    { ShipTo.Country: \"UK\" }                => \"£3.99 UK delivery\",\n    { Total: > 200 }                         => \"Free international\",\n    _                                        => \"£9.99 international\"\n};\nConsole.WriteLine(delivery); \u002F\u002F Free UK delivery\n```\n\n**Rule of thumb:** Design your domain model with records when you know it will be\npattern-matched. The combination of value equality, deconstruction, and immutability\nmakes records the natural complement to switch expressions.\n",{"id":69,"difficulty":14,"q":70,"a":71},"pattern-matching-null","How does pattern matching handle `null` values?","C# pattern matching is **null-safe by design**. Type patterns never match `null`\n(a null value does not have a type in the pattern sense). The `null` literal pattern\nexplicitly matches null. The `not null` pattern is the clean way to check non-null.\n\n```csharp\nobject? value = null;\n\n\u002F\u002F Type pattern never matches null:\nif (value is string s)   \u002F\u002F false — null is not a string\n    Console.WriteLine(s);\n\n\u002F\u002F Explicit null pattern:\nstring desc = value switch\n{\n    null    => \"was null\",   \u002F\u002F explicit null arm\n    int n   => $\"int: {n}\",\n    string s => $\"string: {s}\",\n    _       => \"other\"\n};\nConsole.WriteLine(desc); \u002F\u002F was null\n\n\u002F\u002F not null pattern (C# 9):\nif (value is not null) Console.WriteLine(\"has value\");\n\n\u002F\u002F Property pattern on nullable reference type:\nstring? name = null;\nif (name is { Length: > 0 })  \u002F\u002F safe — false for null, no NullReferenceException\n    Console.WriteLine($\"Name: {name}\");\n\n\u002F\u002F Preferred null check in modern C#:\nstring? s2 = GetValue();\nstring upper = s2 is { Length: > 0 } ? s2.ToUpper() : \"(empty)\";\n```\n\n**Rule of thumb:** Use `is null` and `is not null` instead of `== null` in modern\nC# — they work correctly with operator-overloaded types and communicate intent\nmore clearly in pattern-matching contexts.\n",{"id":73,"difficulty":25,"q":74,"a":75},"constant-pattern","What is a constant pattern in C# and how is it used?","A **constant pattern** tests whether a value equals a compile-time constant —\na literal number, string, character, boolean, enum value, or `null`. It is the\npattern equivalent of an equality check and is used implicitly in most `switch` arms.\n\n```csharp\n\u002F\u002F Constant patterns in a switch expression:\nint code = 404;\nstring message = code switch\n{\n    200 => \"OK\",\n    301 => \"Moved Permanently\",\n    404 => \"Not Found\",           \u002F\u002F constant pattern: matches integer 404\n    500 => \"Internal Server Error\",\n    _   => \"Unknown\"\n};\nConsole.WriteLine(message); \u002F\u002F Not Found\n\n\u002F\u002F Constant pattern in is-expression:\nobject obj = true;\nif (obj is true)    Console.WriteLine(\"it is true\");\nif (obj is false)   Console.WriteLine(\"it is false\");\n\n\u002F\u002F Enum constant pattern:\nDayOfWeek day = DayOfWeek.Monday;\nbool isWeekday = day switch\n{\n    DayOfWeek.Saturday or DayOfWeek.Sunday => false,\n    _ => true  \u002F\u002F all other constant enum values\n};\n\n\u002F\u002F String constant pattern (case-sensitive):\nstring role = \"Admin\";\nbool canDelete = role switch\n{\n    \"Admin\"     => true,\n    \"Moderator\" => false,\n    _           => false\n};\n\n\u002F\u002F Note: constant patterns use == semantics including operator overloads.\n\u002F\u002F For string comparisons, use when guards if you need case-insensitive matching:\nbool isAdmin = role switch\n{\n    var r when r.Equals(\"admin\", StringComparison.OrdinalIgnoreCase) => true,\n    _ => false\n};\n```\n\n**Rule of thumb:** Constant patterns are implicit in switch arms — every literal value\nin a switch is a constant pattern. For strings that need case-insensitive matching,\nfall back to a `when` guard because constant patterns always use ordinal, case-sensitive\nequality.\n",{"id":77,"difficulty":14,"q":78,"a":79},"pattern-matching-exhaustiveness","What is exhaustiveness checking in switch expressions and how does it affect safety?","A `switch` expression is **exhaustive** when the compiler can prove every possible\ninput is covered by some arm. If a value reaches a switch expression with no matching\narm at runtime, a `SwitchExpressionException` is thrown. The compiler warns at build\ntime when exhaustiveness cannot be proven.\n\n```csharp\n\u002F\u002F Compiler warns: switch expression does not handle all values of 'DayOfWeek'\nstring kind = DateTime.Now.DayOfWeek switch\n{\n    DayOfWeek.Saturday => \"Weekend\",\n    DayOfWeek.Sunday   => \"Weekend\",\n    \u002F\u002F Warning: missing cases for Monday–Friday\n};\n\n\u002F\u002F Fix 1: add explicit arms for all values\nstring kind2 = DateTime.Now.DayOfWeek switch\n{\n    DayOfWeek.Saturday or DayOfWeek.Sunday => \"Weekend\",\n    _                                        => \"Weekday\"  \u002F\u002F discard covers the rest\n};\n\n\u002F\u002F Fix 2: throw for unexpected values — documents intent\nOrderStatus status = GetStatus();\nstring label = status switch\n{\n    OrderStatus.Pending   => \"Awaiting payment\",\n    OrderStatus.Confirmed => \"Processing\",\n    OrderStatus.Shipped   => \"On the way\",\n    OrderStatus.Delivered => \"Delivered\",\n    _ => throw new ArgumentOutOfRangeException(nameof(status), status, null)\n    \u002F\u002F Warning: if a new enum value is added and this switch is not updated,\n    \u002F\u002F the runtime exception surfaces immediately.\n};\n\n\u002F\u002F Sealed class hierarchies: compiler can verify exhaustiveness over subtypes\nabstract record Shape;\nsealed record Circle(double Radius) : Shape;\nsealed record Square(double Side)  : Shape;\n\ndouble area = new Circle(5) switch\n{\n    Circle c => Math.PI * c.Radius * c.Radius,\n    Square s => s.Side * s.Side\n    \u002F\u002F No discard needed — compiler knows only Circle and Square exist\n};\n```\n\n**Rule of thumb:** Always include a `_` (discard) arm that throws\n`ArgumentOutOfRangeException` when matching enums or open class hierarchies. This\nturns a silent missing-case bug (wrong result) into a loud runtime failure — and with\nsealed hierarchies the compiler enforces exhaustiveness for you.\n",{"id":81,"difficulty":82,"q":83,"a":84},"pattern-matching-performance","hard","How does pattern matching perform compared to if-else chains and what are the compiler optimisations?","The C# compiler and JIT optimizer treat `switch` expressions and pattern-matching\nconstructs as opportunities to generate **jump tables**, **binary searches**, or\n**type-check inlining** — often significantly faster than equivalent if-else chains.\n\n```csharp\n\u002F\u002F if-else chain — sequential: O(n) comparisons in the worst case\nstring DescribeIf(int n)\n{\n    if (n == 1) return \"one\";\n    if (n == 2) return \"two\";\n    if (n == 3) return \"three\";\n    return \"other\";\n}\n\n\u002F\u002F switch expression on integers — compiler may emit a jump table: O(1)\nstring DescribeSwitch(int n) => n switch\n{\n    1 => \"one\",\n    2 => \"two\",\n    3 => \"three\",\n    _ => \"other\"\n};\n\n\u002F\u002F Type patterns — compiler orders checks by frequency hints and inheritance depth.\n\u002F\u002F Sealed subtypes enable devirtualisation; the JIT can inline the check.\nstatic double Area(Shape shape) => shape switch\n{\n    Circle c    => Math.PI * c.Radius * c.Radius,\n    Rectangle r => r.Width * r.Height,\n    _           => throw new NotSupportedException()\n};\n\u002F\u002F Note: put the most common case first to reduce average comparison count.\n\n\u002F\u002F Property patterns — compiled to a sequence of property reads + comparisons;\n\u002F\u002F no special optimisation, but still cleaner than nested ifs.\nbool IsHighValueUkOrder(Order o) =>\n    o is { ShipTo.Country: \"UK\", Total: > 500 };\n\u002F\u002F Equivalent to: o.ShipTo.Country == \"UK\" && o.Total > 500\n\n\u002F\u002F Constant integer\u002Fchar\u002Fstring switch — jump table when values are dense,\n\u002F\u002F binary search when sparse. Both are faster than sequential if-else.\n```\n\n**Rule of thumb:** Prefer `switch` expressions over if-else chains for three or more\nconstant alternatives — the compiler can emit faster code (jump tables \u002F binary search).\nFor type patterns, list the most frequently matched types first. Avoid micro-optimising\npattern matching before profiling; the clarity gain alone usually justifies it.\n",15,null,{"description":11},"C# pattern matching interview questions — switch expressions, type patterns, property patterns, list patterns, and when guards.","dotnet\u002Fcsharp-core\u002Fpattern-matching","C# Core","csharp-core","2026-06-23","1U7fNXc1VQPn9L4eyYDt9IO5PviJ8F1RuhyA2kUTc1I",[95,99,102,103,107],{"subtopic":96,"path":97,"order":98},"Async \u002F Await","\u002Fdotnet\u002Fcsharp-core\u002Fasync-await",1,{"subtopic":100,"path":101,"order":12},"Delegates & Events","\u002Fdotnet\u002Fcsharp-core\u002Fdelegates-events",{"subtopic":6,"path":21,"order":20},{"subtopic":104,"path":105,"order":106},"Collections","\u002Fdotnet\u002Fcsharp-core\u002Fcollections",4,{"subtopic":108,"path":109,"order":110},"Exception Handling","\u002Fdotnet\u002Fcsharp-core\u002Fexceptions",5,{"path":112,"title":113},"\u002Fblog\u002Fdotnet-pattern-matching-switch-expressions","C# Pattern Matching and Switch Expressions",1782244117985]