RefactorMCP catalog

Convert Switch Statement to Switch Expression

Turns a switch statement whose every section produces a value into a switch expression that is returned or assigned.

Precondition

  • The caret is on the switch statement, between switch and its opening brace.
  • Every section, braced or not, is one of:
  • return value;
  • variable = value; break;, the same variable in every such section;
  • throw exception;
  • The sections either all return or all assign; sections that throw may be mixed with either.
  • A switch expression throws for a value no arm matches, where a switch statement does nothing. So the switch must have a default section, or, when its sections return, be followed directly by a return or throw that becomes the discard arm.

Transformation

  • The statement becomes return x switch { ... }; or variable = x switch { ... };.
  • Each section becomes an arm, in order, with the default arm last as _:
  • case c: becomes the constant pattern c;
  • case pattern when condition: keeps its pattern and when clause;
  • several labels become one pattern joined by or, unless one of them has a when clause or declares a variable, in which case each becomes its own arm with the same value;
  • throw e; becomes the throw expression throw e.
  • The return or throw after a switch with no default becomes the _ arm and is removed.
  • Arms are written one per line, with a trailing comma after the last.

Preserved

  • Behaviour: patterns are tried in the same order, and the default section, wherever it was, only runs when no case matches, as _ does last.
  • Comments on a section's labels and statements are written above its arm; a comment at the end of the section's statement stays at the end of the arm.

Limitations

  • A declaration immediately before an assigning switch is not merged into it; combine them with Join Declaration and Assignment.
  • A value spanning several lines keeps its line breaks and their original indentation.

Error codes

CodeMeaning
not-a-switchthe caret is not on a switch statement
unsupported-sectiona section does something other than return a value, assign one variable and break, or throw
different-targetsthe sections assign different variables
no-defaultthere is no default section and no return after the switch to become one

Cases

·

Diffs show each file from before to after. /*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.

#assignments

A switch whose every section assigns one variable and breaks becomes an assignment of a switch expression

success
TargetPricing.cs, the /*^*/ caret
Pricing.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Pricing
66 {
77 public void Print(string tier)
88 {
99 decimal rate;
10− /*^*/switch (tier)
10+ rate = tier switch
1111 {
12− case "gold":
13− rate = 0.2m;
14− break;
15− case "silver":
16− rate = 0.1m;
17− break;
18− default:
19− rate = 0m;
20− break;
21− }
12+ "gold" => 0.2m,
13+ "silver" => 0.1m,
14+ _ => 0m,
15+ };
2216
2317 Console.WriteLine(rate);
2418 }
2519 }
2620 }

#different-targets-rejected

Refuses sections that assign different variables

refusal
TargetSample.cs, the /*^*/ caret
Refusesdifferent-targets; every file is left unchanged
Sample.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Sample
6 {
7 public void Print(int code)
8 {
9 string name = "";
10 string other = "";
11 /*^*/switch (code)
12 {
13 case 1:
14 name = "one";
15 break;
16 default:
17 other = "other";
18 break;
19 }
20
21 Console.WriteLine(name + other);
22 }
23 }
24 }

#following-return-becomes-default

Without a default, the return after the switch becomes the discard arm

success
TargetShapes.cs, the /*^*/ caret
Shapes.csmodified
11 namespace Shop
22 {
33 public class Circle
44 {
55 public double Radius;
66 }
77
88 public class Shapes
99 {
1010 public string Describe(object shape)
1111 {
12− /*^*/switch (shape)
12+ return shape switch
1313 {
14− case null:
15− return "nothing";
16− case Circle circle when circle.Radius > 10:
17− return "big circle";
18− case Circle:
19− return "circle";
20− }
21−
22− return "unknown";
14+ null => "nothing",
15+ Circle circle when circle.Radius > 10 => "big circle",
16+ Circle => "circle",
17+ _ => "unknown",
18+ };
2319 }
2420 }
2521 }

#mixed-sections-rejected

Refuses sections that do more than return or assign one value

refusal
TargetSample.cs, the /*^*/ caret
Refusesunsupported-section; every file is left unchanged
Sample.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Sample
6 {
7 public string Name(int code)
8 {
9 /*^*/switch (code)
10 {
11 case 1:
12 Console.WriteLine("one");
13 return "one";
14 default:
15 return "other";
16 }
17 }
18 }
19 }

#no-default-rejected

Refuses a switch with no default and nothing after it to become the discard arm, since a switch expression would throw

refusal
TargetSample.cs, the /*^*/ caret
Refusesno-default; every file is left unchanged
Sample.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Sample
6 {
7 public void Print(int code)
8 {
9 string name = "";
10 /*^*/switch (code)
11 {
12 case 1:
13 name = "one";
14 break;
15 case 2:
16 name = "two";
17 break;
18 }
19
20 Console.WriteLine(name);
21 }
22 }
23 }

#not-a-switch-rejected

Refuses a caret that is not on a switch statement

refusal
TargetSample.cs, the /*^*/ caret
Refusesnot-a-switch; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 public int Twice(int value)
6 {
7 /*^*/return value * 2;
8 }
9 }
10 }

#returns

A switch whose every section returns becomes a returned switch expression

success
TargetShipping.cs, the /*^*/ caret
Shipping.csmodified
11 namespace Shop
22 {
33 public class Shipping
44 {
55 public string Zone(int code)
66 {
7− /*^*/switch (code)
7+ return code switch
88 {
9− case 1:
10− return "local";
11− case 2:
12− case 3:
13− return "national";
14− default:
15− return "international";
16− }
9+ 1 => "local",
10+ 2 or 3 => "national",
11+ _ => "international",
12+ };
1713 }
1814 }
1915 }

#throws-and-comments

A section that throws becomes a throw expression, and comments on sections stay with their arms

success
TargetParser.cs, the /*^*/ caret
Parser.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Parser
66 {
77 public int Digit(char c)
88 {
9− /*^*/switch (c)
9+ return c switch
1010 {
1111 // The usual case.
12− case '0':
13− return 0;
14− case '1':
15− return 1; // binary is enough
16− default:
17− throw new ArgumentException("not a digit");
18− }
12+ '0' => 0,
13+ '1' => 1, // binary is enough
14+ _ => throw new ArgumentException("not a digit"),
15+ };
1916 }
2017 }
2118 }