RefactorMCP catalog

Replace Type Code with Enum

Replaces a set of int or string constants that name kinds of something, such as Employee.Engineer = 0 and Employee.Manager = 1, with an enum, and retypes every declaration that carries one of those codes.

This is a generator: it adds a type, and its after/ pins one chosen design (the enum's placement, naming and values described below) rather than the only correct answer. With int constants the program behaves as before except where a code is formatted or converted: ToString() now gives the member's name. With string constants the values themselves disappear, so anything that printed, stored or parsed the strings changes.

Arguments

ArgumentRequiredMeaning
nameyesThe enum's name, such as EmployeeType
constantsyesThe names of the constants, as an array, in the order the enum lists them

The target is the type declaring the constants, by symbol: "target": { "symbol": "T:Staff.Employee" }.

Precondition

  • Every named constant is a const field of the type.
  • The constants are all int or all string.
  • The type's namespace has no type named name, and the type's folder has no file named <name>.cs.
  • The result compiles: a code that is used as a number or a string, such as _type * 10 or _type == "E", cannot become an enum.

Transformation

  • A public enum named name is created in <name>.cs beside the type's file, in the type's namespace and in the same namespace style (file-scoped or block). Its members have the constants' names, in the order given, each with the comments that were above its constant.
  • Int values are written only when they are not 0, 1, 2 and so on in order; otherwise every member is given its old value. String values are dropped.
  • The constants are removed from the type.
  • Every reference to a constant, anywhere in the solution, becomes the enum member, such as Employee.Manager or Manager becoming EmployeeType.Manager, with a using added where the enum's namespace is not in scope.
  • A field, property, parameter, local or method return type of the code's type is retyped to the enum when a code flows into or out of it: a constant is assigned to it, used to initialise it, passed to it, compared with it by == or !=, used as a label or pattern in a switch or is on it, or returned from it. The same then applies, until nothing changes, to declarations that exchange values with one already retyped. A nullable string? becomes a nullable enum, so null checks keep compiling. Locals declared with var keep var.

Preserved

  • The members, their order and layout, apart from the removed constants.
  • Comments next to the retyped declarations and the rewritten references.
  • Which branch each code takes in comparisons and switches.

Limitations

  • Only the flows listed above are followed. A code stored in a collection, passed to a method outside the solution, or converted explicitly is not followed, and if that leaves an error the refactoring refuses rather than inserting casts.
  • A member that overrides or implements another is retyped on its own; if its base or interface cannot follow, the refactoring refuses.
  • Nullable int codes (int?) are not recognised as carriers.

Error codes

CodeMeaning
constant-not-foundthe type has no constant with a given name
mixed-typesthe constants mix int and string values
not-a-type-codea constant is neither an int nor a string
type-already-existsthe namespace already has a type, or the folder a file, with the enum's name
breaks-compilationthe result would not compile, typically because a code is used as a number or string

Cases

·

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

#breaks-compilation

Refuses when the code is used as a number, which an enum does not allow

refusal
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Salesman"]
Refusesbreaks-compilation; every file is left unchanged
Employee.csinput
1 namespace Staff;
2
3 public class Employee
4 {
5 public const int Engineer = 0;
6 public const int Salesman = 1;
7
8 private readonly int _type;
9
10 public Employee(int type)
11 {
12 _type = type;
13 }
14
15 public bool Sells() => _type == Salesman;
16
17 public int Grade() => _type * 10;
18 }

#comments-and-blank-lines

Carries the constants' comments to the enum members and keeps the comments beside the retyped declarations

success
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Salesman"]
Employee.csmodified
11 namespace Staff;
22
33 /// <summary>Someone on the payroll.</summary>
44 public class Employee
55 {
6− // The kinds of employee.
7− /// <summary>Writes the software.</summary>
8− public const int Engineer = 0;
6+ private EmployeeType _type; // set once
97
10− /// <summary>Sells the software.</summary>
11− public const int Salesman = 1;
12−
13− private int _type; // set once
14−
15− public Employee(int type) => _type = type;
8+ public Employee(EmployeeType type) => _type = type;
169
1710 /* commission applies */
18− public bool Sells() => _type == Salesman; // not engineers
11+ public bool Sells() => _type == EmployeeType.Salesman; // not engineers
1912 }
EmployeeType.csadded
1+namespace Staff;
2+
3+public enum EmployeeType
4+{
5+ // The kinds of employee.
6+ /// <summary>Writes the software.</summary>
7+ Engineer,
8+
9+ /// <summary>Sells the software.</summary>
10+ Salesman
11+}

#constant-not-found

Refuses a constant the type does not declare

refusal
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Director"]
Refusesconstant-not-found mentioning “Director”; every file is left unchanged
Employee.csinput
1 namespace Staff;
2
3 public class Employee
4 {
5 public const int Engineer = 0;
6 public const int Salesman = 1;
7 public const string Title = "Staff";
8 public const double Rate = 0.5;
9
10 private readonly int _type;
11
12 public Employee(int type)
13 {
14 _type = type;
15 }
16
17 public bool Sells() => _type == Salesman;
18 }

#explicit-values

Keeps the constants' values when they are not 0, 1, 2 and so on, in the block namespace style of the type's file

success
Targetsymbol T:Shipping.Parcel
Arguments
name"DeliverySpeed"
constants["Standard", "Express", "Overnight"]
DeliverySpeed.csadded
1+namespace Shipping
2+{
3+ public enum DeliverySpeed
4+ {
5+ Standard = 1,
6+ Express = 2,
7+ Overnight = 4
8+ }
9+}
Parcel.csmodified
11 namespace Shipping
22 {
33 public class Parcel
44 {
5− public const int Standard = 1;
6− public const int Express = 2;
7− public const int Overnight = 4;
8−
9− public Parcel(int speed)
5+ public Parcel(DeliverySpeed speed)
106 {
117 Speed = speed;
128 }
139
14− public int Speed { get; }
10+ public DeliverySpeed Speed { get; }
1511
16− public bool IsFast() => Speed != Standard;
12+ public bool IsFast() => Speed != DeliverySpeed.Standard;
1713 }
1814 }

#int-constants

Replaces int constants with an enum in a new file and retypes the field, parameter and property that carry them

success
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Salesman", "Manager"]
Employee.csmodified
11 namespace Staff;
22
33 public class Employee
44 {
5− public const int Engineer = 0;
6− public const int Salesman = 1;
7− public const int Manager = 2;
5+ private readonly EmployeeType _type;
86
9− private readonly int _type;
10−
11− public Employee(int type)
7+ public Employee(EmployeeType type)
128 {
139 _type = type;
1410 }
1511
16− public int Type => _type;
12+ public EmployeeType Type => _type;
1713
1814 public int Bonus()
1915 {
2016 switch (_type)
2117 {
22− case Engineer:
18+ case EmployeeType.Engineer:
2319 return 100;
24− case Salesman:
20+ case EmployeeType.Salesman:
2521 return 200;
2622 default:
2723 return 300;
2824 }
2925 }
3026
31− public bool IsManager() => _type == Manager;
27+ public bool IsManager() => _type == EmployeeType.Manager;
3228 }
EmployeeType.csadded
1+namespace Staff;
2+
3+public enum EmployeeType
4+{
5+ Engineer,
6+ Salesman,
7+ Manager
8+}

#mixed-types

Refuses int and string constants together, which cannot be one type code

refusal
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Title"]
Refusesmixed-types; every file is left unchanged
Employee.csinput
1 namespace Staff;
2
3 public class Employee
4 {
5 public const int Engineer = 0;
6 public const int Salesman = 1;
7 public const string Title = "Staff";
8 public const double Rate = 0.5;
9
10 private readonly int _type;
11
12 public Employee(int type)
13 {
14 _type = type;
15 }
16
17 public bool Sells() => _type == Salesman;
18 }

#not-a-type-code

Refuses a constant that is neither an int nor a string

refusal
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Rate"]
Refusesnot-a-type-code mentioning “Rate”; every file is left unchanged
Employee.csinput
1 namespace Staff;
2
3 public class Employee
4 {
5 public const int Engineer = 0;
6 public const int Salesman = 1;
7 public const string Title = "Staff";
8 public const double Rate = 0.5;
9
10 private readonly int _type;
11
12 public Employee(int type)
13 {
14 _type = type;
15 }
16
17 public bool Sells() => _type == Salesman;
18 }

#nullable-annotations

A nullable string code becomes a nullable enum, so the null check still compiles

success
Targetsymbol T:Library.Member
Arguments
name"MemberStatus"
constants["Student", "Staff"]
Projectnullable enable
Member.csmodified
11 namespace Library;
22
33 public class Member
44 {
5− public const string Student = "S";
6− public const string Staff = "T";
5+ private MemberStatus? _status;
76
8− private string? _status;
9−
10− public void Enrol(string status)
7+ public void Enrol(MemberStatus status)
118 {
129 _status = status;
1310 }
1411
15− public bool IsStudent => _status == Student;
12+ public bool IsStudent => _status == MemberStatus.Student;
1613
1714 public bool IsEnrolled => _status != null;
1815 }
MemberStatus.csadded
1+namespace Library;
2+
3+public enum MemberStatus
4+{
5+ Student,
6+ Staff
7+}

#references-in-another-file

Retargets qualified references in another namespace and retypes the local, auto-property and overload the codes flow into

success
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Manager"]
Employee.csmodified
11 namespace Staff;
22
33 public class Employee
44 {
5− public const int Engineer = 0;
6− public const int Manager = 1;
7−
8− public Employee(int type, string name)
5+ public Employee(EmployeeType type, string name)
96 {
107 Type = type;
118 Name = name;
129 }
1310
14− public int Type { get; }
11+ public EmployeeType Type { get; }
1512
1613 public string Name { get; }
1714 }
EmployeeType.csadded
1+namespace Staff;
2+
3+public enum EmployeeType
4+{
5+ Engineer,
6+ Manager
7+}
Payroll.csmodified
11 using Staff;
22
33 namespace Accounts;
44
55 public class Payroll
66 {
7− public Employee Hire(string name) => new Employee(Employee.Manager, name);
7+ public Employee Hire(string name) => new Employee(EmployeeType.Manager, name);
88
99 public string Describe(Employee employee)
1010 {
11− int code = employee.Type;
12− if (code == Employee.Engineer)
11+ EmployeeType code = employee.Type;
12+ if (code == EmployeeType.Engineer)
1313 return "engineer";
1414 return Label(employee.Type);
1515 }
1616
17− private static string Label(int code) => code == Employee.Manager ? "manager" : "staff";
17+ private static string Label(EmployeeType code) => code == EmployeeType.Manager ? "manager" : "staff";
1818
1919 private static string Label(string text) => text;
2020 }

#string-constants

Replaces string constants, dropping their values, and retargets the constant patterns of a switch expression

success
Targetsymbol T:Library.Loan
Arguments
name"LoanKind"
constants["Book", "Film"]
Loan.csmodified
11 namespace Library;
22
33 public class Loan
44 {
5− public const string Book = "B";
6− public const string Film = "F";
7−
8− public Loan(string kind)
5+ public Loan(LoanKind kind)
96 {
107 Kind = kind;
118 }
129
13− public string Kind { get; }
10+ public LoanKind Kind { get; }
1411
1512 public int Days()
1613 {
1714 return Kind switch
1815 {
19− Book => 21,
20− Film => 7,
16+ LoanKind.Book => 21,
17+ LoanKind.Film => 7,
2118 _ => 14,
2219 };
2320 }
2421 }
LoanKind.csadded
1+namespace Library;
2+
3+public enum LoanKind
4+{
5+ Book,
6+ Film
7+}

#type-already-exists

Refuses an enum name the namespace already uses

refusal
Targetsymbol T:Staff.Employee
Arguments
name"EmployeeType"
constants["Engineer", "Salesman"]
Refusestype-already-exists; every file is left unchanged
Employee.csinput
1 namespace Staff;
2
3 public class Employee
4 {
5 public const int Engineer = 0;
6 public const int Salesman = 1;
7 public const string Title = "Staff";
8 public const double Rate = 0.5;
9
10 private readonly int _type;
11
12 public Employee(int type)
13 {
14 _type = type;
15 }
16
17 public bool Sells() => _type == Salesman;
18 }
EmployeeType.csinput
1 namespace Staff;
2
3 public class EmployeeType
4 {
5 }