[{"data":1,"prerenderedAt":1252},["ShallowReactive",2],{"blog-\u002Fblog\u002Fdotnet-pattern-matching-switch-expressions":3},{"id":4,"title":5,"body":6,"description":1237,"difficulty":1238,"extension":1239,"framework":1240,"frameworkSlug":1241,"meta":1242,"navigation":142,"order":51,"path":1243,"qaPath":1244,"seo":1245,"stem":1246,"subtopic":1247,"topic":1248,"topicSlug":1249,"updated":1250,"__hash__":1251},"blog\u002Fblog\u002Fdotnet-pattern-matching-switch-expressions.md","C# Pattern Matching and Switch Expressions",{"type":7,"value":8,"toc":1223},"minimark",[9,14,18,22,25,107,110,185,189,207,301,312,316,319,395,409,413,416,492,503,506,530,534,541,665,669,696,796,803,807,813,861,870,874,877,996,1000,1007,1107,1111,1114,1184,1188,1219],[10,11,13],"h2",{"id":12},"why-pattern-matching-keeps-getting-better-with-each-c-version","Why pattern matching keeps getting better with each C# version",[15,16,17],"p",{},"Every major C# release since C# 7 has expanded pattern matching. Interviewers test it\nbecause it reflects modern idiomatic C# — candidates who know only C# 6 write verbose,\nmulti-line type-checking code that modern C# collapses to a single expression. This guide\ncovers every pattern type and when to reach for each.",[10,19,21],{"id":20},"the-problem-pattern-matching-solves","The problem pattern matching solves",[15,23,24],{},"Pre-C# 7 type-dispatching code:",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-csharp shiki shiki-themes github-light github-dark","\u002F\u002F Old style — verbose and error-prone:\nobject value = GetValue();\nif (value is int)\n{\n    int n = (int)value;\n    Console.WriteLine(n * 2);\n}\nelse if (value is string)\n{\n    string s = (string)value;\n    Console.WriteLine(s.ToUpper());\n}\n","csharp","",[33,34,35,43,49,55,61,67,73,79,85,90,96,102],"code",{"__ignoreMap":31},[36,37,40],"span",{"class":38,"line":39},"line",1,[36,41,42],{},"\u002F\u002F Old style — verbose and error-prone:\n",[36,44,46],{"class":38,"line":45},2,[36,47,48],{},"object value = GetValue();\n",[36,50,52],{"class":38,"line":51},3,[36,53,54],{},"if (value is int)\n",[36,56,58],{"class":38,"line":57},4,[36,59,60],{},"{\n",[36,62,64],{"class":38,"line":63},5,[36,65,66],{},"    int n = (int)value;\n",[36,68,70],{"class":38,"line":69},6,[36,71,72],{},"    Console.WriteLine(n * 2);\n",[36,74,76],{"class":38,"line":75},7,[36,77,78],{},"}\n",[36,80,82],{"class":38,"line":81},8,[36,83,84],{},"else if (value is string)\n",[36,86,88],{"class":38,"line":87},9,[36,89,60],{},[36,91,93],{"class":38,"line":92},10,[36,94,95],{},"    string s = (string)value;\n",[36,97,99],{"class":38,"line":98},11,[36,100,101],{},"    Console.WriteLine(s.ToUpper());\n",[36,103,105],{"class":38,"line":104},12,[36,106,78],{},[15,108,109],{},"Modern C# 7+:",[26,111,113],{"className":28,"code":112,"language":30,"meta":31,"style":31},"\u002F\u002F Type pattern — test and bind in one step:\nif (value is int n)\n    Console.WriteLine(n * 2);\nelse if (value is string s)\n    Console.WriteLine(s.ToUpper());\n\n\u002F\u002F Better: switch expression (C# 8):\nstring result = value switch\n{\n    int n    => $\"int: {n * 2}\",\n    string s => s.ToUpper(),\n    null     => \"null\",\n    _        => value.GetType().Name\n};\n",[33,114,115,120,125,129,134,138,144,149,154,158,163,168,173,179],{"__ignoreMap":31},[36,116,117],{"class":38,"line":39},[36,118,119],{},"\u002F\u002F Type pattern — test and bind in one step:\n",[36,121,122],{"class":38,"line":45},[36,123,124],{},"if (value is int n)\n",[36,126,127],{"class":38,"line":51},[36,128,72],{},[36,130,131],{"class":38,"line":57},[36,132,133],{},"else if (value is string s)\n",[36,135,136],{"class":38,"line":63},[36,137,101],{},[36,139,140],{"class":38,"line":69},[36,141,143],{"emptyLinePlaceholder":142},true,"\n",[36,145,146],{"class":38,"line":75},[36,147,148],{},"\u002F\u002F Better: switch expression (C# 8):\n",[36,150,151],{"class":38,"line":81},[36,152,153],{},"string result = value switch\n",[36,155,156],{"class":38,"line":87},[36,157,60],{},[36,159,160],{"class":38,"line":92},[36,161,162],{},"    int n    => $\"int: {n * 2}\",\n",[36,164,165],{"class":38,"line":98},[36,166,167],{},"    string s => s.ToUpper(),\n",[36,169,170],{"class":38,"line":104},[36,171,172],{},"    null     => \"null\",\n",[36,174,176],{"class":38,"line":175},13,[36,177,178],{},"    _        => value.GetType().Name\n",[36,180,182],{"class":38,"line":181},14,[36,183,184],{},"};\n",[10,186,188],{"id":187},"switch-expression-vs-switch-statement","switch expression vs switch statement",[15,190,191,192,195,196,200,201,195,203,206],{},"The ",[33,193,194],{},"switch"," ",[197,198,199],"strong",{},"statement"," executes side effects. The ",[33,202,194],{},[197,204,205],{},"expression"," produces\na value — it is an expression, not a statement.",[26,208,210],{"className":28,"code":209,"language":30,"meta":31,"style":31},"\u002F\u002F switch statement (C# 1+): imperative, branches, requires break\nstring label;\nswitch (status)\n{\n    case OrderStatus.New:      label = \"New\";      break;\n    case OrderStatus.Shipped:  label = \"Shipped\";  break;\n    case OrderStatus.Delivered: label = \"Delivered\"; break;\n    default:                   label = \"Unknown\";  break;\n}\n\n\u002F\u002F switch expression (C# 8+): expression, no break, exhaustiveness checked by compiler\nstring label2 = status switch\n{\n    OrderStatus.New       => \"New\",\n    OrderStatus.Shipped   => \"Shipped\",\n    OrderStatus.Delivered => \"Delivered\",\n    _                     => \"Unknown\",\n};\n",[33,211,212,217,222,227,231,236,241,246,251,255,259,264,269,273,278,284,290,296],{"__ignoreMap":31},[36,213,214],{"class":38,"line":39},[36,215,216],{},"\u002F\u002F switch statement (C# 1+): imperative, branches, requires break\n",[36,218,219],{"class":38,"line":45},[36,220,221],{},"string label;\n",[36,223,224],{"class":38,"line":51},[36,225,226],{},"switch (status)\n",[36,228,229],{"class":38,"line":57},[36,230,60],{},[36,232,233],{"class":38,"line":63},[36,234,235],{},"    case OrderStatus.New:      label = \"New\";      break;\n",[36,237,238],{"class":38,"line":69},[36,239,240],{},"    case OrderStatus.Shipped:  label = \"Shipped\";  break;\n",[36,242,243],{"class":38,"line":75},[36,244,245],{},"    case OrderStatus.Delivered: label = \"Delivered\"; break;\n",[36,247,248],{"class":38,"line":81},[36,249,250],{},"    default:                   label = \"Unknown\";  break;\n",[36,252,253],{"class":38,"line":87},[36,254,78],{},[36,256,257],{"class":38,"line":92},[36,258,143],{"emptyLinePlaceholder":142},[36,260,261],{"class":38,"line":98},[36,262,263],{},"\u002F\u002F switch expression (C# 8+): expression, no break, exhaustiveness checked by compiler\n",[36,265,266],{"class":38,"line":104},[36,267,268],{},"string label2 = status switch\n",[36,270,271],{"class":38,"line":175},[36,272,60],{},[36,274,275],{"class":38,"line":181},[36,276,277],{},"    OrderStatus.New       => \"New\",\n",[36,279,281],{"class":38,"line":280},15,[36,282,283],{},"    OrderStatus.Shipped   => \"Shipped\",\n",[36,285,287],{"class":38,"line":286},16,[36,288,289],{},"    OrderStatus.Delivered => \"Delivered\",\n",[36,291,293],{"class":38,"line":292},17,[36,294,295],{},"    _                     => \"Unknown\",\n",[36,297,299],{"class":38,"line":298},18,[36,300,184],{},[15,302,303,304,307,308,311],{},"The compiler warns if a switch expression might not cover all inputs (it is not exhaustive).\nOmitting the discard ",[33,305,306],{},"_"," arm when values are possible means a ",[33,309,310],{},"SwitchExpressionException","\nat runtime.",[10,313,315],{"id":314},"type-patterns","Type patterns",[15,317,318],{},"A type pattern tests the runtime type and binds the value:",[26,320,322],{"className":28,"code":321,"language":30,"meta":31,"style":31},"Shape shape = GetShape();\n\n\u002F\u002F Type pattern in switch expression:\ndouble area = shape switch\n{\n    Circle c    => Math.PI * c.Radius * c.Radius,\n    Rectangle r => r.Width * r.Height,\n    Triangle t  => 0.5 * t.Base * t.Height,\n    _           => throw new ArgumentException($\"Unknown shape: {shape.GetType().Name}\")\n};\n\n\u002F\u002F Inheritance: tests runtime type, not compile-time type\nobject obj = new Dog();\nif (obj is Animal a)          \u002F\u002F matches — Dog IS-A Animal\n    Console.WriteLine(a.GetType().Name); \u002F\u002F \"Dog\"\n",[33,323,324,329,333,338,343,347,352,357,362,367,371,375,380,385,390],{"__ignoreMap":31},[36,325,326],{"class":38,"line":39},[36,327,328],{},"Shape shape = GetShape();\n",[36,330,331],{"class":38,"line":45},[36,332,143],{"emptyLinePlaceholder":142},[36,334,335],{"class":38,"line":51},[36,336,337],{},"\u002F\u002F Type pattern in switch expression:\n",[36,339,340],{"class":38,"line":57},[36,341,342],{},"double area = shape switch\n",[36,344,345],{"class":38,"line":63},[36,346,60],{},[36,348,349],{"class":38,"line":69},[36,350,351],{},"    Circle c    => Math.PI * c.Radius * c.Radius,\n",[36,353,354],{"class":38,"line":75},[36,355,356],{},"    Rectangle r => r.Width * r.Height,\n",[36,358,359],{"class":38,"line":81},[36,360,361],{},"    Triangle t  => 0.5 * t.Base * t.Height,\n",[36,363,364],{"class":38,"line":87},[36,365,366],{},"    _           => throw new ArgumentException($\"Unknown shape: {shape.GetType().Name}\")\n",[36,368,369],{"class":38,"line":92},[36,370,184],{},[36,372,373],{"class":38,"line":98},[36,374,143],{"emptyLinePlaceholder":142},[36,376,377],{"class":38,"line":104},[36,378,379],{},"\u002F\u002F Inheritance: tests runtime type, not compile-time type\n",[36,381,382],{"class":38,"line":175},[36,383,384],{},"object obj = new Dog();\n",[36,386,387],{"class":38,"line":181},[36,388,389],{},"if (obj is Animal a)          \u002F\u002F matches — Dog IS-A Animal\n",[36,391,392],{"class":38,"line":280},[36,393,394],{},"    Console.WriteLine(a.GetType().Name); \u002F\u002F \"Dog\"\n",[15,396,397,398,401,402,405,406,408],{},"Type patterns never match ",[33,399,400],{},"null"," — if ",[33,403,404],{},"shape"," is null, none of the arms match and the\ndiscard ",[33,407,306],{}," catches it.",[10,410,412],{"id":411},"property-patterns","Property patterns",[15,414,415],{},"Property patterns match objects by the value of their properties:",[26,417,419],{"className":28,"code":418,"language":30,"meta":31,"style":31},"record Address(string Country, string PostCode);\nrecord Customer(string Name, Address Address, bool IsPremium, decimal Balance);\n\nCustomer c = new(\"Alice\", new Address(\"UK\", \"EC1A\"), true, 500m);\n\nstring offer = c switch\n{\n    \u002F\u002F Nested property pattern (C# 10 dot notation):\n    { IsPremium: true, Address.Country: \"UK\", Balance: > 1000 } => \"Gold UK member\",\n    { IsPremium: true, Address.Country: \"UK\" }                   => \"Premium UK\",\n    { IsPremium: true }                                           => \"Premium\",\n    { Address.Country: \"UK\" }                                     => \"Standard UK\",\n    _                                                             => \"Standard\"\n};\nConsole.WriteLine(offer); \u002F\u002F Premium UK\n",[33,420,421,426,431,435,440,444,449,453,458,463,468,473,478,483,487],{"__ignoreMap":31},[36,422,423],{"class":38,"line":39},[36,424,425],{},"record Address(string Country, string PostCode);\n",[36,427,428],{"class":38,"line":45},[36,429,430],{},"record Customer(string Name, Address Address, bool IsPremium, decimal Balance);\n",[36,432,433],{"class":38,"line":51},[36,434,143],{"emptyLinePlaceholder":142},[36,436,437],{"class":38,"line":57},[36,438,439],{},"Customer c = new(\"Alice\", new Address(\"UK\", \"EC1A\"), true, 500m);\n",[36,441,442],{"class":38,"line":63},[36,443,143],{"emptyLinePlaceholder":142},[36,445,446],{"class":38,"line":69},[36,447,448],{},"string offer = c switch\n",[36,450,451],{"class":38,"line":75},[36,452,60],{},[36,454,455],{"class":38,"line":81},[36,456,457],{},"    \u002F\u002F Nested property pattern (C# 10 dot notation):\n",[36,459,460],{"class":38,"line":87},[36,461,462],{},"    { IsPremium: true, Address.Country: \"UK\", Balance: > 1000 } => \"Gold UK member\",\n",[36,464,465],{"class":38,"line":92},[36,466,467],{},"    { IsPremium: true, Address.Country: \"UK\" }                   => \"Premium UK\",\n",[36,469,470],{"class":38,"line":98},[36,471,472],{},"    { IsPremium: true }                                           => \"Premium\",\n",[36,474,475],{"class":38,"line":104},[36,476,477],{},"    { Address.Country: \"UK\" }                                     => \"Standard UK\",\n",[36,479,480],{"class":38,"line":175},[36,481,482],{},"    _                                                             => \"Standard\"\n",[36,484,485],{"class":38,"line":181},[36,486,184],{},[36,488,489],{"class":38,"line":280},[36,490,491],{},"Console.WriteLine(offer); \u002F\u002F Premium UK\n",[15,493,494,495,498,499,502],{},"C# 10 introduced extended property patterns: ",[33,496,497],{},"{ Address.Country: \"UK\" }"," instead of the\nverbose C# 8 form ",[33,500,501],{},"{ Address: { Country: \"UK\" } }",".",[15,504,505],{},"Combine property patterns with type patterns:",[26,507,509],{"className":28,"code":508,"language":30,"meta":31,"style":31},"object obj = new Customer(\"Bob\", new Address(\"US\", \"NY\"), false, 0m);\n\nif (obj is Customer { Name: var name, Balance: 0, IsPremium: false })\n    Console.WriteLine($\"Inactive customer: {name}\");\n",[33,510,511,516,520,525],{"__ignoreMap":31},[36,512,513],{"class":38,"line":39},[36,514,515],{},"object obj = new Customer(\"Bob\", new Address(\"US\", \"NY\"), false, 0m);\n",[36,517,518],{"class":38,"line":45},[36,519,143],{"emptyLinePlaceholder":142},[36,521,522],{"class":38,"line":51},[36,523,524],{},"if (obj is Customer { Name: var name, Balance: 0, IsPremium: false })\n",[36,526,527],{"class":38,"line":57},[36,528,529],{},"    Console.WriteLine($\"Inactive customer: {name}\");\n",[10,531,533],{"id":532},"positional-patterns","Positional patterns",[15,535,536,537,540],{},"Positional patterns use a type's ",[33,538,539],{},"Deconstruct"," method. Records get deconstruction for free:",[26,542,544],{"className":28,"code":543,"language":30,"meta":31,"style":31},"record Point(int X, int Y);\n\nPoint p = new(3, -5);\n\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 Tuples also support positional patterns:\n(string role, bool isActive) user = (\"admin\", false);\nstring access = user switch\n{\n    (\"admin\", true)  => \"Full access\",\n    (\"admin\", false) => \"Account suspended\",\n    (_, true)        => \"Limited access\",\n    _                => \"No access\"\n};\n",[33,545,546,551,555,560,564,569,573,578,583,588,593,598,603,607,612,616,621,626,631,636,642,648,654,660],{"__ignoreMap":31},[36,547,548],{"class":38,"line":39},[36,549,550],{},"record Point(int X, int Y);\n",[36,552,553],{"class":38,"line":45},[36,554,143],{"emptyLinePlaceholder":142},[36,556,557],{"class":38,"line":51},[36,558,559],{},"Point p = new(3, -5);\n",[36,561,562],{"class":38,"line":57},[36,563,143],{"emptyLinePlaceholder":142},[36,565,566],{"class":38,"line":63},[36,567,568],{},"string quadrant = p switch\n",[36,570,571],{"class":38,"line":69},[36,572,60],{},[36,574,575],{"class":38,"line":75},[36,576,577],{},"    (0, 0)     => \"Origin\",\n",[36,579,580],{"class":38,"line":81},[36,581,582],{},"    (> 0, > 0) => \"Q1\",\n",[36,584,585],{"class":38,"line":87},[36,586,587],{},"    (\u003C 0, > 0) => \"Q2\",\n",[36,589,590],{"class":38,"line":92},[36,591,592],{},"    (\u003C 0, \u003C 0) => \"Q3\",\n",[36,594,595],{"class":38,"line":98},[36,596,597],{},"    (> 0, \u003C 0) => \"Q4\",\n",[36,599,600],{"class":38,"line":104},[36,601,602],{},"    _           => \"On axis\"\n",[36,604,605],{"class":38,"line":175},[36,606,184],{},[36,608,609],{"class":38,"line":181},[36,610,611],{},"Console.WriteLine(quadrant); \u002F\u002F Q4\n",[36,613,614],{"class":38,"line":280},[36,615,143],{"emptyLinePlaceholder":142},[36,617,618],{"class":38,"line":286},[36,619,620],{},"\u002F\u002F Tuples also support positional patterns:\n",[36,622,623],{"class":38,"line":292},[36,624,625],{},"(string role, bool isActive) user = (\"admin\", false);\n",[36,627,628],{"class":38,"line":298},[36,629,630],{},"string access = user switch\n",[36,632,634],{"class":38,"line":633},19,[36,635,60],{},[36,637,639],{"class":38,"line":638},20,[36,640,641],{},"    (\"admin\", true)  => \"Full access\",\n",[36,643,645],{"class":38,"line":644},21,[36,646,647],{},"    (\"admin\", false) => \"Account suspended\",\n",[36,649,651],{"class":38,"line":650},22,[36,652,653],{},"    (_, true)        => \"Limited access\",\n",[36,655,657],{"class":38,"line":656},23,[36,658,659],{},"    _                => \"No access\"\n",[36,661,663],{"class":38,"line":662},24,[36,664,184],{},[10,666,668],{"id":667},"relational-and-logical-patterns-c-9","Relational and logical patterns (C# 9)",[15,670,671,672,675,676,675,679,675,682,685,686,675,689,675,692,695],{},"Relational patterns compare with ",[33,673,674],{},"\u003C",", ",[33,677,678],{},"\u003C=",[33,680,681],{},">",[33,683,684],{},">=",". Logical patterns combine with\n",[33,687,688],{},"and",[33,690,691],{},"or",[33,693,694],{},"not",":",[26,697,699],{"className":28,"code":698,"language":30,"meta":31,"style":31},"int temperature = 72;\n\nstring weather = temperature switch\n{\n    \u003C 0           => \"Freezing\",\n    >= 0 and \u003C 10 => \"Cold\",\n    >= 10 and \u003C 20 => \"Cool\",\n    >= 20 and \u003C 30 => \"Comfortable\",\n    >= 30 and \u003C 40 => \"Hot\",\n    _             => \"Extreme heat\"\n};\n\n\u002F\u002F Logical 'or':\nDayOfWeek day = DateTime.Today.DayOfWeek;\nbool isWeekend = day is DayOfWeek.Saturday or DayOfWeek.Sunday;\n\n\u002F\u002F Logical 'not':\nstring? name = GetName();\nif (name is not null and { Length: > 0 })\n    Console.WriteLine(name.ToUpper());\n",[33,700,701,706,710,715,719,724,729,734,739,744,749,753,757,762,767,772,776,781,786,791],{"__ignoreMap":31},[36,702,703],{"class":38,"line":39},[36,704,705],{},"int temperature = 72;\n",[36,707,708],{"class":38,"line":45},[36,709,143],{"emptyLinePlaceholder":142},[36,711,712],{"class":38,"line":51},[36,713,714],{},"string weather = temperature switch\n",[36,716,717],{"class":38,"line":57},[36,718,60],{},[36,720,721],{"class":38,"line":63},[36,722,723],{},"    \u003C 0           => \"Freezing\",\n",[36,725,726],{"class":38,"line":69},[36,727,728],{},"    >= 0 and \u003C 10 => \"Cold\",\n",[36,730,731],{"class":38,"line":75},[36,732,733],{},"    >= 10 and \u003C 20 => \"Cool\",\n",[36,735,736],{"class":38,"line":81},[36,737,738],{},"    >= 20 and \u003C 30 => \"Comfortable\",\n",[36,740,741],{"class":38,"line":87},[36,742,743],{},"    >= 30 and \u003C 40 => \"Hot\",\n",[36,745,746],{"class":38,"line":92},[36,747,748],{},"    _             => \"Extreme heat\"\n",[36,750,751],{"class":38,"line":98},[36,752,184],{},[36,754,755],{"class":38,"line":104},[36,756,143],{"emptyLinePlaceholder":142},[36,758,759],{"class":38,"line":175},[36,760,761],{},"\u002F\u002F Logical 'or':\n",[36,763,764],{"class":38,"line":181},[36,765,766],{},"DayOfWeek day = DateTime.Today.DayOfWeek;\n",[36,768,769],{"class":38,"line":280},[36,770,771],{},"bool isWeekend = day is DayOfWeek.Saturday or DayOfWeek.Sunday;\n",[36,773,774],{"class":38,"line":286},[36,775,143],{"emptyLinePlaceholder":142},[36,777,778],{"class":38,"line":292},[36,779,780],{},"\u002F\u002F Logical 'not':\n",[36,782,783],{"class":38,"line":298},[36,784,785],{},"string? name = GetName();\n",[36,787,788],{"class":38,"line":633},[36,789,790],{},"if (name is not null and { Length: > 0 })\n",[36,792,793],{"class":38,"line":638},[36,794,795],{},"    Console.WriteLine(name.ToUpper());\n",[15,797,798,799,802],{},"Relational patterns only work with comparable value types (numeric types, ",[33,800,801],{},"char",").",[10,804,806],{"id":805},"when-guards","when guards",[15,808,809,812],{},[33,810,811],{},"when"," adds an arbitrary boolean condition to a pattern arm. It runs only if the pattern\nalready matched:",[26,814,816],{"className":28,"code":815,"language":30,"meta":31,"style":31},"\u002F\u002F Use when for logic that can't be expressed as a pure pattern:\nstatic string ClassifyOrder(Order order) => order switch\n{\n    { Status: OrderStatus.Shipped } when order.DaysInTransit > 14 => \"Potentially lost\",\n    { Status: OrderStatus.Shipped }                                 => \"In transit\",\n    { Total: var t } when t > 10_000                               => \"High-value order\",\n    { IsPaid: false, Total: > 0 }                                  => \"Payment pending\",\n    _                                                               => \"Standard\"\n};\n",[33,817,818,823,828,832,837,842,847,852,857],{"__ignoreMap":31},[36,819,820],{"class":38,"line":39},[36,821,822],{},"\u002F\u002F Use when for logic that can't be expressed as a pure pattern:\n",[36,824,825],{"class":38,"line":45},[36,826,827],{},"static string ClassifyOrder(Order order) => order switch\n",[36,829,830],{"class":38,"line":51},[36,831,60],{},[36,833,834],{"class":38,"line":57},[36,835,836],{},"    { Status: OrderStatus.Shipped } when order.DaysInTransit > 14 => \"Potentially lost\",\n",[36,838,839],{"class":38,"line":63},[36,840,841],{},"    { Status: OrderStatus.Shipped }                                 => \"In transit\",\n",[36,843,844],{"class":38,"line":69},[36,845,846],{},"    { Total: var t } when t > 10_000                               => \"High-value order\",\n",[36,848,849],{"class":38,"line":75},[36,850,851],{},"    { IsPaid: false, Total: > 0 }                                  => \"Payment pending\",\n",[36,853,854],{"class":38,"line":81},[36,855,856],{},"    _                                                               => \"Standard\"\n",[36,858,859],{"class":38,"line":87},[36,860,184],{},[15,862,191,863,865,866,869],{},[33,864,811],{}," filter runs ",[197,867,868],{},"after"," the pattern is tested, so the matched variable is\navailable in the guard expression.",[10,871,873],{"id":872},"list-patterns-c-11","List patterns (C# 11)",[15,875,876],{},"List patterns match arrays and lists by structure:",[26,878,880],{"className":28,"code":879,"language":30,"meta":31,"style":31},"int[] data = { 1, 2, 3, 4, 5 };\n\n\u002F\u002F Exact match:\nbool exact = data is [1, 2, 3, 4, 5]; \u002F\u002F true\n\n\u002F\u002F Partial match with slice:\nbool starts = data is [1, 2, ..];     \u002F\u002F true — starts with 1, 2\nbool ends   = data is [.., 4, 5];     \u002F\u002F true — ends with 4, 5\nbool middle = data is [_, _, 3, ..];  \u002F\u002F true — third element is 3\n\n\u002F\u002F Capture with var:\nif (data is [var head, .. var rest])\n    Console.WriteLine($\"head={head}, rest has {rest.Length} elements\"); \u002F\u002F head=1, rest has 4\n\n\u002F\u002F Routing example:\nstring[] segments = { \"api\", \"products\", \"42\" };\nstring response = segments switch\n{\n    [\"api\", \"products\", var id] => $\"Get product {id}\",\n    [\"api\", \"products\"]         => \"List products\",\n    [\"api\", ..]                 => \"Other API endpoint\",\n    _                           => \"Not found\"\n};\nConsole.WriteLine(response); \u002F\u002F Get product 42\n",[33,881,882,887,891,896,901,905,910,915,920,925,929,934,939,944,948,953,958,963,967,972,977,982,987,991],{"__ignoreMap":31},[36,883,884],{"class":38,"line":39},[36,885,886],{},"int[] data = { 1, 2, 3, 4, 5 };\n",[36,888,889],{"class":38,"line":45},[36,890,143],{"emptyLinePlaceholder":142},[36,892,893],{"class":38,"line":51},[36,894,895],{},"\u002F\u002F Exact match:\n",[36,897,898],{"class":38,"line":57},[36,899,900],{},"bool exact = data is [1, 2, 3, 4, 5]; \u002F\u002F true\n",[36,902,903],{"class":38,"line":63},[36,904,143],{"emptyLinePlaceholder":142},[36,906,907],{"class":38,"line":69},[36,908,909],{},"\u002F\u002F Partial match with slice:\n",[36,911,912],{"class":38,"line":75},[36,913,914],{},"bool starts = data is [1, 2, ..];     \u002F\u002F true — starts with 1, 2\n",[36,916,917],{"class":38,"line":81},[36,918,919],{},"bool ends   = data is [.., 4, 5];     \u002F\u002F true — ends with 4, 5\n",[36,921,922],{"class":38,"line":87},[36,923,924],{},"bool middle = data is [_, _, 3, ..];  \u002F\u002F true — third element is 3\n",[36,926,927],{"class":38,"line":92},[36,928,143],{"emptyLinePlaceholder":142},[36,930,931],{"class":38,"line":98},[36,932,933],{},"\u002F\u002F Capture with var:\n",[36,935,936],{"class":38,"line":104},[36,937,938],{},"if (data is [var head, .. var rest])\n",[36,940,941],{"class":38,"line":175},[36,942,943],{},"    Console.WriteLine($\"head={head}, rest has {rest.Length} elements\"); \u002F\u002F head=1, rest has 4\n",[36,945,946],{"class":38,"line":181},[36,947,143],{"emptyLinePlaceholder":142},[36,949,950],{"class":38,"line":280},[36,951,952],{},"\u002F\u002F Routing example:\n",[36,954,955],{"class":38,"line":286},[36,956,957],{},"string[] segments = { \"api\", \"products\", \"42\" };\n",[36,959,960],{"class":38,"line":292},[36,961,962],{},"string response = segments switch\n",[36,964,965],{"class":38,"line":298},[36,966,60],{},[36,968,969],{"class":38,"line":633},[36,970,971],{},"    [\"api\", \"products\", var id] => $\"Get product {id}\",\n",[36,973,974],{"class":38,"line":638},[36,975,976],{},"    [\"api\", \"products\"]         => \"List products\",\n",[36,978,979],{"class":38,"line":644},[36,980,981],{},"    [\"api\", ..]                 => \"Other API endpoint\",\n",[36,983,984],{"class":38,"line":650},[36,985,986],{},"    _                           => \"Not found\"\n",[36,988,989],{"class":38,"line":656},[36,990,184],{},[36,992,993],{"class":38,"line":662},[36,994,995],{},"Console.WriteLine(response); \u002F\u002F Get product 42\n",[10,997,999],{"id":998},"deconstruction-and-the-var-pattern","Deconstruction and the var pattern",[15,1001,1002,1003,1006],{},"Deconstruction unpacks a type into components. The ",[33,1004,1005],{},"var"," pattern binds any value without\ntype-checking (always succeeds, including null):",[26,1008,1010],{"className":28,"code":1009,"language":30,"meta":31,"style":31},"\u002F\u002F Deconstruction in assignment:\nvar (x, y) = new Point(3, 7);\nvar (name, age) = (\"Alice\", 30);\n\n\u002F\u002F var pattern — bind for use in when guard:\nobject obj = \"hello world\";\nstring desc = obj switch\n{\n    null          => \"null\",\n    var s when s is string str && str.Contains(' ') => $\"sentence: {str}\",\n    var s         => $\"other: {s}\"\n};\n\n\u002F\u002F Discard in deconstruction:\nvar (_, height) = new Rectangle(10, 5); \u002F\u002F only care about height\n\n\u002F\u002F foreach deconstruction:\nvar people = new[] { (\"Alice\", 30), (\"Bob\", 25) };\nforeach (var (n, a) in people)\n    Console.WriteLine($\"{n} is {a}\");\n",[33,1011,1012,1017,1022,1027,1031,1036,1041,1046,1050,1055,1060,1065,1069,1073,1078,1083,1087,1092,1097,1102],{"__ignoreMap":31},[36,1013,1014],{"class":38,"line":39},[36,1015,1016],{},"\u002F\u002F Deconstruction in assignment:\n",[36,1018,1019],{"class":38,"line":45},[36,1020,1021],{},"var (x, y) = new Point(3, 7);\n",[36,1023,1024],{"class":38,"line":51},[36,1025,1026],{},"var (name, age) = (\"Alice\", 30);\n",[36,1028,1029],{"class":38,"line":57},[36,1030,143],{"emptyLinePlaceholder":142},[36,1032,1033],{"class":38,"line":63},[36,1034,1035],{},"\u002F\u002F var pattern — bind for use in when guard:\n",[36,1037,1038],{"class":38,"line":69},[36,1039,1040],{},"object obj = \"hello world\";\n",[36,1042,1043],{"class":38,"line":75},[36,1044,1045],{},"string desc = obj switch\n",[36,1047,1048],{"class":38,"line":81},[36,1049,60],{},[36,1051,1052],{"class":38,"line":87},[36,1053,1054],{},"    null          => \"null\",\n",[36,1056,1057],{"class":38,"line":92},[36,1058,1059],{},"    var s when s is string str && str.Contains(' ') => $\"sentence: {str}\",\n",[36,1061,1062],{"class":38,"line":98},[36,1063,1064],{},"    var s         => $\"other: {s}\"\n",[36,1066,1067],{"class":38,"line":104},[36,1068,184],{},[36,1070,1071],{"class":38,"line":175},[36,1072,143],{"emptyLinePlaceholder":142},[36,1074,1075],{"class":38,"line":181},[36,1076,1077],{},"\u002F\u002F Discard in deconstruction:\n",[36,1079,1080],{"class":38,"line":280},[36,1081,1082],{},"var (_, height) = new Rectangle(10, 5); \u002F\u002F only care about height\n",[36,1084,1085],{"class":38,"line":286},[36,1086,143],{"emptyLinePlaceholder":142},[36,1088,1089],{"class":38,"line":292},[36,1090,1091],{},"\u002F\u002F foreach deconstruction:\n",[36,1093,1094],{"class":38,"line":298},[36,1095,1096],{},"var people = new[] { (\"Alice\", 30), (\"Bob\", 25) };\n",[36,1098,1099],{"class":38,"line":633},[36,1100,1101],{},"foreach (var (n, a) in people)\n",[36,1103,1104],{"class":38,"line":638},[36,1105,1106],{},"    Console.WriteLine($\"{n} is {a}\");\n",[10,1108,1110],{"id":1109},"null-safety-in-patterns","Null safety in patterns",[15,1112,1113],{},"Type patterns are null-safe by design — null never matches a type pattern:",[26,1115,1117],{"className":28,"code":1116,"language":30,"meta":31,"style":31},"object? value = null;\n\nif (value is string s) \u002F\u002F false — null is not a string; no NullReferenceException\n    Console.WriteLine(s);\n\n\u002F\u002F Prefer 'is null' and 'is not null' over == null:\nstring? name = GetName();\nif (name is not null and { Length: > 0 }) \u002F\u002F short-circuit: null check first\n    Console.WriteLine(name.ToUpper());\n\n\u002F\u002F Property pattern on nullable:\nAddress? addr = GetAddress();\nif (addr is { Country: \"UK\", PostCode.Length: > 0 }) \u002F\u002F safe — false if addr is null\n    Console.WriteLine(\"Valid UK address\");\n",[33,1118,1119,1124,1128,1133,1138,1142,1147,1151,1156,1160,1164,1169,1174,1179],{"__ignoreMap":31},[36,1120,1121],{"class":38,"line":39},[36,1122,1123],{},"object? value = null;\n",[36,1125,1126],{"class":38,"line":45},[36,1127,143],{"emptyLinePlaceholder":142},[36,1129,1130],{"class":38,"line":51},[36,1131,1132],{},"if (value is string s) \u002F\u002F false — null is not a string; no NullReferenceException\n",[36,1134,1135],{"class":38,"line":57},[36,1136,1137],{},"    Console.WriteLine(s);\n",[36,1139,1140],{"class":38,"line":63},[36,1141,143],{"emptyLinePlaceholder":142},[36,1143,1144],{"class":38,"line":69},[36,1145,1146],{},"\u002F\u002F Prefer 'is null' and 'is not null' over == null:\n",[36,1148,1149],{"class":38,"line":75},[36,1150,785],{},[36,1152,1153],{"class":38,"line":81},[36,1154,1155],{},"if (name is not null and { Length: > 0 }) \u002F\u002F short-circuit: null check first\n",[36,1157,1158],{"class":38,"line":87},[36,1159,795],{},[36,1161,1162],{"class":38,"line":92},[36,1163,143],{"emptyLinePlaceholder":142},[36,1165,1166],{"class":38,"line":98},[36,1167,1168],{},"\u002F\u002F Property pattern on nullable:\n",[36,1170,1171],{"class":38,"line":104},[36,1172,1173],{},"Address? addr = GetAddress();\n",[36,1175,1176],{"class":38,"line":175},[36,1177,1178],{},"if (addr is { Country: \"UK\", PostCode.Length: > 0 }) \u002F\u002F safe — false if addr is null\n",[36,1180,1181],{"class":38,"line":181},[36,1182,1183],{},"    Console.WriteLine(\"Valid UK address\");\n",[10,1185,1187],{"id":1186},"recap","Recap",[15,1189,1190,1191,1194,1195,675,1197,675,1199,675,1201,675,1203,1205,1206,1208,1209,1211,1212,1214,1215,1218],{},"Pattern matching in C# has evolved from simple ",[33,1192,1193],{},"is","-checks into a powerful expression-\nbased dispatch system. Switch expressions are value-producing, exhaustive-checked\nalternatives to switch statements. Type patterns test and bind runtime types, safely\nhandling null. Property patterns dispatch on object shape, making business-rule code\ndeclarative and readable. Positional patterns work with records and deconstruction.\nRelational and logical patterns (",[33,1196,688],{},[33,1198,691],{},[33,1200,694],{},[33,1202,674],{},[33,1204,684],{},") express range and\nexclusion conditions without verbose when guards. List patterns (C# 11) match sequences\nby structure. ",[33,1207,811],{}," guards add arbitrary conditions when pure patterns aren't enough.\nThe ",[33,1210,1005],{}," pattern captures any value for use in a guard expression. In all cases, type\npatterns never match null — use explicit ",[33,1213,400],{}," or ",[33,1216,1217],{},"not null"," patterns for null handling.",[1220,1221,1222],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":31,"searchDepth":45,"depth":45,"links":1224},[1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236],{"id":12,"depth":45,"text":13},{"id":20,"depth":45,"text":21},{"id":187,"depth":45,"text":188},{"id":314,"depth":45,"text":315},{"id":411,"depth":45,"text":412},{"id":532,"depth":45,"text":533},{"id":667,"depth":45,"text":668},{"id":805,"depth":45,"text":806},{"id":872,"depth":45,"text":873},{"id":998,"depth":45,"text":999},{"id":1109,"depth":45,"text":1110},{"id":1186,"depth":45,"text":1187},"How C# pattern matching works — switch expressions, type and property patterns, list patterns, and how the compiler generates efficient code for each form.","medium","md",".NET Core","dotnet",{},"\u002Fblog\u002Fdotnet-pattern-matching-switch-expressions","\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching",{"title":5,"description":1237},"blog\u002Fdotnet-pattern-matching-switch-expressions","Pattern Matching","C# Core","csharp-core","2026-06-23","a0Ew3_395tAuqxMxwhqpmCMnSHhl9flfJa34DfrFnaU",1782244086561]