RefactorMCP catalog

Convert Switch Expression to Switch Statement

Turns a switch expression that is the whole value of a return, an assignment or a local's initialiser into a switch statement.

Precondition

  • The caret is on the switch expression, on its governing expression or its switch keyword.
  • The switch expression is exactly one of:
  • the value of a return statement;
  • the right-hand side of a simple assignment used as a statement;
  • the initialiser of a local declared on its own, without using or const, whose type can be named.

Transformation

  • Each arm becomes a section, in order:
  • a constant pattern c becomes case c:;
  • the alternatives of an or pattern become separate labels, unless the arm has a when clause or declares a variable;
  • any other pattern, with its when clause, becomes a pattern label;
  • _ becomes default:.
  • A section returns the arm's value, or assigns it and breaks. A throw expression becomes a throw statement.
  • A local initialised by the switch expression is declared first, with its type written out in place of var, and assigned in each section.
  • A switch expression with no discard arm throws SwitchExpressionException for an unmatched value, so a default section throwing it is added, with the value when it is a simple name. This keeps a switch that is exhaustive without a discard, such as one on bool, from falling off the end.

Preserved

  • Behaviour, including the exception for an unmatched value.
  • Comments above an arm move above its section.

Limitations

  • Switch expressions nested in larger expressions, passed as arguments, or used as expression bodies are refused; extract them to a local first.
  • The added SwitchExpressionException carries the unmatched value only when the governing expression is a simple name or member access, to avoid evaluating it twice.

Error codes

CodeMeaning
not-a-switchthe caret is not on a switch expression
unsupported-contextthe switch expression is not the whole value of a return, an assignment statement or a single local's initialiser
anonymous-typethe local it initialises has an anonymous type, which cannot be declared separately

Cases

·

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

#anonymous-type-rejected

Refuses a local of anonymous type, which cannot be declared before the switch

refusal
TargetSample.cs, the /*^*/ caret
Refusesanonymous-type; 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 var item = code /*^*/switch
10 {
11 1 => new { Name = "one" },
12 _ => new { Name = "other" },
13 };
14 Console.WriteLine(item.Name);
15 }
16 }
17 }

#assigned

An assigned switch expression becomes sections that assign and break, and a throw arm becomes a throw statement

success
TargetPricing.cs, the /*^*/ caret
Pricing.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Pricing
66 {
77 private decimal _rate;
88
99 public decimal Rate => _rate;
1010
1111 public void Apply(string tier)
1212 {
13− _rate = tier /*^*/switch
13+ switch (tier)
1414 {
15− "gold" => 0.2m,
16− "silver" => 0.1m,
17− _ => throw new ArgumentException("unknown tier"),
18− };
15+ case "gold":
16+ _rate = 0.2m;
17+ break;
18+ case "silver":
19+ _rate = 0.1m;
20+ break;
21+ default:
22+ throw new ArgumentException("unknown tier");
23+ }
1924 }
2025 }
2126 }

#comments-kept

Comments above an arm move above its section, and comments before the statement stay before the switch

success
TargetParser.cs, the /*^*/ caret
Parser.csmodified
11 namespace Shop
22 {
33 public class Parser
44 {
55 public int Digit(char c)
66 {
77 // Only binary digits are expected.
8− return c /*^*/switch
8+ switch (c)
99 {
1010 // The usual case.
11− '0' => 0,
12− '1' => 1,
13− _ => -1,
14− };
11+ case '0':
12+ return 0;
13+ case '1':
14+ return 1;
15+ default:
16+ return -1;
17+ }
1518 }
1619 }
1720 }

#declaration

A local initialised by a switch expression is declared with its type first, and when clauses are kept

success
TargetShapes.cs, the /*^*/ caret
Shapes.csmodified
11 using System;
22
33 namespace Shop
44 {
55 public class Circle
66 {
77 public double Radius;
88 }
99
1010 public class Shapes
1111 {
1212 public void Print(object shape)
1313 {
14− var name = shape /*^*/switch
14+ string name;
15+ switch (shape)
1516 {
16− Circle circle when circle.Radius > 10 => "big circle",
17− Circle => "circle",
18− _ => "unknown",
19− };
17+ case Circle circle when circle.Radius > 10:
18+ name = "big circle";
19+ break;
20+ case Circle:
21+ name = "circle";
22+ break;
23+ default:
24+ name = "unknown";
25+ break;
26+ }
2027 Console.WriteLine(name);
2128 }
2229 }
2330 }

#nested-expression-rejected

Refuses a switch expression that is part of a larger expression

refusal
TargetSample.cs, the /*^*/ caret
Refusesunsupported-context; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 public string Name(int code)
6 {
7 return "code " + (code /*^*/switch
8 {
9 1 => "one",
10 _ => "other",
11 });
12 }
13 }
14 }

#no-discard-arm

Without a discard arm, the default section throws the exception the switch expression would have thrown

success
TargetSign.cs, the /*^*/ caret
Sign.csmodified
1+using System.Runtime.CompilerServices;
2+
13 namespace Shop
24 {
35 public class Sign
46 {
57 public string Describe(bool positive)
68 {
7− return positive /*^*/switch
9+ switch (positive)
810 {
9− true => "positive",
10− false => "not positive",
11− };
11+ case true:
12+ return "positive";
13+ case false:
14+ return "not positive";
15+ default:
16+ throw new SwitchExpressionException(positive);
17+ }
1218 }
1319 }
1420 }

#not-a-switch-rejected

Refuses a caret that is not on a switch expression

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 }

#returned

A returned switch expression becomes a switch statement whose sections return, or patterns becoming separate labels

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