RefactorMCP catalog

Move Static Method

Moves a static method to another type, named by the caller, creating that type as a static class when it does not exist.

Arguments

ArgumentMeaning
target.symbolthe method, such as M:Shop.Order.Vat(System.Decimal)
tothe target type: a simple name, or a namespace-qualified one
stubtrue (the default) leaves a delegating method behind; false removes it and updates every use
fileoptional: the file for a target type that has to be created (default <Type>.cs beside the source)

Precondition

  • The method is static and not a partial method.
  • The target type, when it exists, is a class or struct declared in the solution, is not the method's own type, and has no method with the same name and parameters.
  • to names one type; a new type's name is a valid identifier and its file does not exist.
  • The target's project can see the old type's project.
  • Without a stub, no use is through null-conditional access or an object initializer.

Transformation

  • The method is appended to the target type, keeping its comments.
  • Static members of the old type that the method uses unqualified are qualified by it (Rate becomes Order.Rate), and private ones become internal. A private method moves as internal.
  • Types and extension methods the method uses are imported into the target file as needed; the old file drops usings only the method needed.
  • A new target type is a static class in the old type's namespace, in the same namespace form, as accessible as the old type.
  • With a stub, the old method keeps its signature and documentation comment and calls the moved one: => TaxRules.Vat(net);.
  • Without a stub, every use, including calls inside the old type and method groups (amounts.Select(Order.Vat)), names the new type, which files in other namespaces import. Explicit type arguments are kept.

Preserved

  • The behaviour of every call.
  • Generic type parameters and their constraints.

Limitations

  • using static imports of the old type are not updated.
  • The method is always appended at the end of the target type.

Error codes

CodeMeaning
method-not-staticthe method is an instance method; Move Instance Method moves it
same-typethe target is the method's own type
member-existsthe target already has a method with that name and parameters
target-not-in-sourcethe target type is not declared in the solution
target-not-classthe target type is not a class or struct
uses-protected-memberthe method uses a protected member of its type
conditional-accesswithout a stub, a use goes through null-conditional access
target-cannot-see-sourcethe target's project cannot see the old type

Cases

·

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

#creates-class

A target class that does not exist is created as a static class in a new file beside the source, in the same namespace

success
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"Pricing"
Order.csmodified
11 namespace Shop;
22
33 public class Order
44 {
5− public static decimal Vat(decimal net) => net * 0.2m;
5+ public static decimal Vat(decimal net) => Pricing.Vat(net);
66 }
Pricing.csadded
1+namespace Shop;
2+
3+public static class Pricing
4+{
5+ public static decimal Vat(decimal net) => net * 0.2m;
6+}

#generic-method

A generic method moves with its constraints; calls keep their type arguments

success
Targetsymbol M:Shop.Order.Larger``1(``0,``0)
Arguments
to"Compare"
stubfalse
Compare.csmodified
1+using System;
2+
13 namespace Shop
24 {
35 public static class Compare
46 {
7+ public static T Larger<T>(T first, T second) where T : IComparable<T> =>
8+ first.CompareTo(second) >= 0 ? first : second;
59 }
610 }
Order.csmodified
1−using System;
2−
31 namespace Shop
42 {
53 public class Order
64 {
7− public static T Larger<T>(T first, T second) where T : IComparable<T> =>
8− first.CompareTo(second) >= 0 ? first : second;
9−
10− public int Biggest(int a, int b) => Larger(a, b) + Larger<int>(b, a);
5+ public int Biggest(int a, int b) => Compare.Larger(a, b) + Compare.Larger<int>(b, a);
116 }
127 }

#member-exists-rejected

Refuses when the target already has a method with the same name and parameters

refusal
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"TaxRules"
Refusesmember-exists; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public static decimal Vat(decimal net) => net * 0.2m;
6 }
7
8 public static class TaxRules
9 {
10 public static decimal Vat(decimal net) => net * 0.25m;
11 }
12 }

#not-static-rejected

Refuses an instance method, which Move Instance Method moves

refusal
Targetsymbol M:Shop.Order.Describe
Arguments
to"Formatting"
Refusesmethod-not-static; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public string Describe() => "order";
6 }
7
8 public static class Formatting
9 {
10 }
11 }

#other-namespace

Moving to a class in another namespace imports what the method needs there, and callers import the new namespace

success
Targetsymbol M:Shop.Order.Largest(System.Collections.Generic.IEnumerable{Shop.Order})
Arguments
to"OrderQueries"
stubfalse
Order.csmodified
1−using System.Collections.Generic;
2−using System.Linq;
3−
41 namespace Shop
52 {
63 public class Order
74 {
85 public decimal Total { get; set; }
9−
10− public static Order Largest(IEnumerable<Order> orders) => orders.OrderByDescending(o => o.Total).First();
116 }
127 }
Queries/OrderQueries.csmodified
1+using System.Collections.Generic;
2+using System.Linq;
3+
14 namespace Shop.Queries
25 {
36 public static class OrderQueries
47 {
8+ public static Order Largest(IEnumerable<Order> orders) => orders.OrderByDescending(o => o.Total).First();
59 }
610 }
Report.csmodified
11 using System.Collections.Generic;
2+using Shop.Queries;
23
34 namespace Shop
45 {
56 public class Report
67 {
7− public decimal Top(List<Order> orders) => Order.Largest(orders).Total;
8+ public decimal Top(List<Order> orders) => OrderQueries.Largest(orders).Total;
89 }
910 }

#same-type-rejected

Refuses to move a method to the class it is already in

refusal
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"Order"
Refusessame-type; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public static decimal Vat(decimal net) => net * 0.2m;
6 }
7 }

#target-not-in-source-rejected

Refuses to move into a type the solution does not declare

refusal
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"System.Math"
Refusestarget-not-in-source; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public static decimal Vat(decimal net) => net * 0.2m;
6 }
7 }

#with-stub

Moves a static method to another class, qualifying what it used from its old class, and leaves a delegating stub

success
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"TaxRules"
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
5− private const decimal Rate = 0.2m;
5+ internal const decimal Rate = 0.2m;
66
7− // VAT is charged on the net amount.
8− public static decimal Vat(decimal net) => Round(net * Rate);
7+ public static decimal Vat(decimal net) => TaxRules.Vat(net);
98
10− private static decimal Round(decimal value) => decimal.Round(value, 2);
9+ internal static decimal Round(decimal value) => decimal.Round(value, 2);
1110 }
1211 }
TaxRules.csmodified
11 namespace Shop
22 {
33 public static class TaxRules
44 {
55 public const string Region = "UK";
6+
7+ // VAT is charged on the net amount.
8+ public static decimal Vat(decimal net) => Order.Round(net * Order.Rate);
69 }
710 }

#without-stub

Without a stub, calls in every file, calls inside the old class and method groups name the new class

success
Targetsymbol M:Shop.Order.Vat(System.Decimal)
Arguments
to"TaxRules"
stubfalse
Checkout.csmodified
11 using System.Collections.Generic;
22 using System.Linq;
33
44 namespace Shop
55 {
66 public class Checkout
77 {
8− public decimal Tax(decimal amount) => Order.Vat(amount);
8+ public decimal Tax(decimal amount) => TaxRules.Vat(amount);
99
10− public IEnumerable<decimal> Taxes(IEnumerable<decimal> amounts) => amounts.Select(Order.Vat);
10+ public IEnumerable<decimal> Taxes(IEnumerable<decimal> amounts) => amounts.Select(TaxRules.Vat);
1111 }
1212 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
55 public decimal Net { get; set; }
66
7− public static decimal Vat(decimal net) => net * 0.2m;
8−
9− public decimal Gross() => Net + Vat(Net);
7+ public decimal Gross() => Net + TaxRules.Vat(Net);
108 }
119 }
TaxRules.csmodified
11 namespace Shop
22 {
33 public static class TaxRules
44 {
5+ public static decimal Vat(decimal net) => net * 0.2m;
56 }
67 }