RefactorMCP catalog

Make Static then Move

Moves an instance method to another class as a static method that takes the instance as a parameter. Useful for moving a method out of a large class when it has no field or parameter of the target type to move through.

Recipe

  1. make-method-static the method, passing the instance: "target": { "symbol": "M:Shop.Order.Describe(System.String)" }.
  2. move-static-method the now static method to the target class: "target": { "symbol": "M:Shop.Order.Describe(Shop.Order,System.String)" }, "arguments": { "to": "Receipts" }.

The second step's symbol has the instance as its first parameter; a method that used no instance member gains no parameter and keeps its id.

Arguments

ArgumentRequiredMeaning
toyesThe target class: a simple or namespace-qualified name. It is created as a static class when it does not exist
namenoThe instance parameter's name; defaults to the class name in camel case
stubnotrue (the default) leaves a static method in the old class that delegates to the moved one; false points every call at the target
filenoThe file for a target class that has to be created; defaults to <to>.cs beside the class

The target is the method, by symbol.

Precondition

  • Make Method Static's precondition: the method is an instance method of a class, not virtual, abstract, an override or an interface implementation, does not call through base, and is not called through null-conditional access or used as a method group.
  • Move Static Method's precondition: the target is a class or struct declared in the solution, or a free name for a new one, other than the method's own class, without a method of the same name and parameters.

Transformation

  • The method becomes static with the instance as its first parameter; uses of instance members go through it (_total becomes order._total).
  • The method moves to the target; private members of the old class it uses become internal.
  • Every call passes the instance: order.Describe("x") becomes Receipts.Describe(order, "x"), or Order.Describe(order, "x") when a stub is kept.

Preserved

  • The result of every call, and the method's comments and documentation.

Limitations

  • With a stub, callers of the old instance method still change to call the static stub, because making the method static changes its signature.
  • Private members the method uses become internal.

Error codes

CodeMeaning
already-staticthe method is already static; Move Static Method moves it
polymorphic-methodthe method is virtual, abstract, an override or an interface implementation
not-a-classthe method belongs to a struct or interface
uses-basethe method calls through base
method-groupthe method is used as a method group
conditional-accessa call uses null-conditional access
name-conflictthe instance parameter's name is already used in the method
same-typethe target is the method's own class
member-existsthe target already has a method with the same name and parameters
target-not-classthe target is not a class or struct
uses-protected-memberthe method uses a protected member of its class

A refusal in either step leaves every file as it was, and names the step that refused.

Cases

·

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

#member-exists-rejected

The target already has a method with the static method's parameters, so the move refuses after the method was made static, and every file is put back

refusal
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
to"Receipts"
Refusesmember-exists mentioning “step 2 (move-static-method)”; every file is left unchanged
Checkout.csinput
1 namespace Shop
2 {
3 public class Checkout
4 {
5 public string Receipt(Order order) => order.Describe("Paid: ");
6 }
7 }
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 private decimal _total;
6
7 public string Currency { get; set; }
8
9 /// <summary>A line for the receipt.</summary>
10 public string Describe(string prefix)
11 {
12 // The total is shown without rounding.
13 return prefix + _total + " " + this.Currency;
14 }
15
16 public string Summary() => Describe("Order: ");
17
18 public void Add(decimal price) => _total += price;
19 }
20 }
Receipts.csinput
1 namespace Shop
2 {
3 public static class Receipts
4 {
5 public static string Describe(Order order, string prefix) => prefix + order.Currency;
6 }
7 }

#polymorphic-method-rejected

A virtual method's callers rely on dispatch through the instance, so making it static refuses

refusal
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
to"Receipts"
Refusespolymorphic-method mentioning “step 1 (make-method-static)”; every file is left unchanged
Checkout.csinput
1 namespace Shop
2 {
3 public class Checkout
4 {
5 public string Receipt(Order order) => order.Describe("Paid: ");
6 }
7 }
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 private decimal _total;
6
7 public string Currency { get; set; }
8
9 /// <summary>A line for the receipt.</summary>
10 public virtual string Describe(string prefix)
11 {
12 // The total is shown without rounding.
13 return prefix + _total + " " + this.Currency;
14 }
15
16 public string Summary() => Describe("Order: ");
17
18 public void Add(decimal price) => _total += price;
19 }
20 }

#recipe

Makes Describe static, taking the order, then moves it to a new Receipts class and points every call at it, as the recipe of primitive steps

successrecipe
Recipe
  1. make-method-static on symbol M:Shop.Order.Describe(System.String)
  2. move-static-method on symbol M:Shop.Order.Describe(Shop.Order,System.String)
    to"Receipts"
    stubfalse
Checkout.csmodified
11 namespace Shop
22 {
33 public class Checkout
44 {
5− public string Receipt(Order order) => order.Describe("Paid: ");
5+ public string Receipt(Order order) => Receipts.Describe(order, "Paid: ");
66 }
77 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
5− private decimal _total;
5+ internal decimal _total;
66
77 public string Currency { get; set; }
88
9− /// <summary>A line for the receipt.</summary>
10− public string Describe(string prefix)
11− {
12− // The total is shown without rounding.
13− return prefix + _total + " " + this.Currency;
14− }
15−
16− public string Summary() => Describe("Order: ");
9+ public string Summary() => Receipts.Describe(this, "Order: ");
1710
1811 public void Add(decimal price) => _total += price;
1912 }
2013 }
Receipts.csadded
1+namespace Shop
2+{
3+ public static class Receipts
4+ {
5+ /// <summary>A line for the receipt.</summary>
6+ public static string Describe(Order order, string prefix)
7+ {
8+ // The total is shown without rounding.
9+ return prefix + order._total + " " + order.Currency;
10+ }
11+ }
12+}

#to-new-class-without-stub

Makes Describe static and moves it to a new Receipts class in one call, pointing every call at it, with the same result as the recipe

success
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
to"Receipts"
stubfalse
Checkout.csmodified
11 namespace Shop
22 {
33 public class Checkout
44 {
5− public string Receipt(Order order) => order.Describe("Paid: ");
5+ public string Receipt(Order order) => Receipts.Describe(order, "Paid: ");
66 }
77 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
5− private decimal _total;
5+ internal decimal _total;
66
77 public string Currency { get; set; }
88
9− /// <summary>A line for the receipt.</summary>
10− public string Describe(string prefix)
11− {
12− // The total is shown without rounding.
13− return prefix + _total + " " + this.Currency;
14− }
15−
16− public string Summary() => Describe("Order: ");
9+ public string Summary() => Receipts.Describe(this, "Order: ");
1710
1811 public void Add(decimal price) => _total += price;
1912 }
2013 }
Receipts.csadded
1+namespace Shop
2+{
3+ public static class Receipts
4+ {
5+ /// <summary>A line for the receipt.</summary>
6+ public static string Describe(Order order, string prefix)
7+ {
8+ // The total is shown without rounding.
9+ return prefix + order._total + " " + order.Currency;
10+ }
11+ }
12+}

#with-stub-to-existing-class

Moves the method into an existing class under a chosen instance parameter name, leaving a static stub so the order's callers only change to pass the instance

success
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
to"Formatting"
name"sale"
Checkout.csmodified
11 namespace Shop
22 {
33 public class Checkout
44 {
5− public string Receipt(Order order) => order.Describe("Paid: ");
5+ public string Receipt(Order order) => Order.Describe(order, "Paid: ");
66 }
77 }
Formatting.csmodified
11 namespace Shop
22 {
33 public class Formatting
44 {
55 public static string Money(decimal amount) => amount.ToString("0.00");
6+
7+ /// <summary>A line for the receipt.</summary>
8+ public static string Describe(Order sale, string prefix)
9+ {
10+ // The total is shown without rounding.
11+ return prefix + sale._total + " " + sale.Currency;
12+ }
613 }
714 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
5− private decimal _total;
5+ internal decimal _total;
66
77 public string Currency { get; set; }
88
99 /// <summary>A line for the receipt.</summary>
10− public string Describe(string prefix)
10+ public static string Describe(Order sale, string prefix)
1111 {
12− // The total is shown without rounding.
13− return prefix + _total + " " + this.Currency;
12+ return Formatting.Describe(sale, prefix);
1413 }
1514
16− public string Summary() => Describe("Order: ");
15+ public string Summary() => Describe(this, "Order: ");
1716
1817 public void Add(decimal price) => _total += price;
1918 }
2019 }