RefactorMCP catalog

Make Method Static

Turns an instance method into a static method of the same class, and updates every call across the solution to pass what the method used to take from the instance.

Arguments

ArgumentMeaning
target.symbolthe method, such as M:Shop.Order.Describe(System.String)
passinstance (the default) or parameters
nameoptional, with pass: instance: the instance parameter's name (default: the type name in camel case)

With pass: instance the method takes the instance as a new first parameter. With pass: parameters each instance field or property it reads becomes a new parameter instead, named after the member in camel case, in the order the members are first used, ahead of the existing parameters.

Precondition

  • The method is an instance method of a class, and is not virtual, abstract, an override or an interface implementation.
  • It does not call through base.
  • The new parameter names are not already used by a parameter or local.
  • No call uses null-conditional access (order?.Describe()), and, when the signature changes, the method is not used as a method group.

With pass: parameters, additionally:

  • The method reads instance fields and properties only: it assigns none, calls no instance method and does not use this on its own.
  • Every member it reads is accessible at every call site.
  • Calls made on an instance name it simply (a local, parameter, field or this), so the instance can be read once per parameter.

Transformation

  • static is added after the accessibility modifiers.
  • With pass: instance, uses of instance members become order.Member, and this becomes order. A method that uses no instance member gains no parameter.
  • With pass: parameters, uses of each member become its parameter.
  • Each call order.Describe(x) becomes Order.Describe(order, x), or Order.Describe(order.Total, order.Currency, x); a call inside the class on this becomes Describe(this, x) or Describe(Total, Currency, x). A call on a constructed generic type names it: Box<int>.Describe(box).

Preserved

  • The result of every call, and the comments and documentation of the method.
  • Nullable annotations of the members passed as parameters.

Limitations

  • Structs are refused, since passing a copy of the instance would lose writes.
  • A recursive method cannot pass its members as parameters.

Error codes

CodeMeaning
already-staticthe method is already static
polymorphic-methodthe method is virtual, abstract, an override or implements an interface
not-a-classthe method belongs to a struct, record struct or interface
uses-basethe method calls through base
assigns-instance-memberwith parameters, the method assigns an instance member
uses-instancewith parameters, the method calls an instance method or uses this
inaccessible-memberwith parameters, a member is not accessible at a call site
complex-receiverwith parameters, a call is made on an expression that is not simple
method-groupthe method is used as a method group and its signature changes
conditional-accessa call uses null-conditional access
name-conflicta new parameter's name is already taken

Cases

·

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

#already-static-rejected

Refuses a method that is already static

refusal
Targetsymbol M:Shop.Order.Tax(System.Decimal)
Refusesalready-static; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public static decimal Tax(decimal amount) => amount * 0.2m;
6 }
7 }

#assigns-member-rejected

Refuses to pass members as parameters when the method assigns one, since the assignment would be lost

refusal
Targetsymbol M:Shop.Order.Add(System.Decimal)
Arguments
pass"parameters"
Refusesassigns-instance-member; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 private decimal _total;
6
7 public void Add(decimal price) => _total += price;
8 }
9 }

#calls-instance-method-rejected

Refuses to pass members as parameters when the method calls an instance method, which needs the instance itself

refusal
Targetsymbol M:Shop.Order.Report
Arguments
pass"parameters"
Refusesuses-instance; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public decimal Total { get; set; }
6
7 public string Format() => Total.ToString();
8
9 public string Report() => "Total: " + Format();
10 }
11 }

#generic-type

In a generic class the instance parameter keeps the type parameters, and calls name the constructed type

success
Targetsymbol M:Shop.Box`1.Describe
Box.csmodified
11 namespace Shop
22 {
33 public class Box<T>
44 {
55 public T Content { get; set; }
66
7− public string Describe() => "Box of " + Content;
7+ public static string Describe(Box<T> box) => "Box of " + box.Content;
88 }
99 }
Shelf.csmodified
11 namespace Shop
22 {
33 public class Shelf
44 {
5− public string Label(Box<int> box) => box.Describe();
5+ public string Label(Box<int> box) => Box<int>.Describe(box);
66 }
77 }

#inaccessible-member-rejected

Refuses to pass a private member as a parameter when a caller outside the class could not read it

refusal
Targetsymbol M:Shop.Order.Describe
Arguments
pass"parameters"
Refusesinaccessible-member; every file is left unchanged
Checkout.csinput
1 namespace Shop
2 {
3 public class Checkout
4 {
5 public string Receipt(Order order) => order.Describe();
6 }
7 }
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 private decimal _total;
6
7 public string Describe() => "Total: " + _total;
8 }
9 }

#no-instance-members

A method that uses no instance member only gains static; calls through an instance are requalified with the type

success
Targetsymbol M:Shop.Order.Tax(System.Decimal)
Checkout.csmodified
11 namespace Shop
22 {
33 public class Checkout
44 {
5− public decimal TaxOn(Order order, decimal amount) => order.Tax(amount);
5+ public decimal TaxOn(Order order, decimal amount) => Order.Tax(amount);
66 }
77 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
55 private const decimal Rate = 0.2m;
66
7− public decimal Tax(decimal amount) => amount * Rate;
7+ public static decimal Tax(decimal amount) => amount * Rate;
88
99 public decimal Gross(decimal net) => net + Tax(net);
1010 }
1111 }

#nullable-parameters

Parameters made from nullable members keep their annotations

success
Targetsymbol M:Shop.Customer.Greeting
Arguments
pass"parameters"
Projectnullable enable
Customer.csmodified
11 namespace Shop
22 {
33 public class Customer
44 {
55 public string Name { get; set; } = "";
66
77 public string? Title { get; set; }
88
9− public string Greeting() => Title is null ? Name : Title + " " + Name;
9+ public static string Greeting(string? title, string name) => title is null ? name : title + " " + name;
1010
11− public string Card() => "Dear " + Greeting();
11+ public string Card() => "Dear " + Greeting(Title, Name);
1212 }
1313 }

#override-rejected

Refuses an override, whose callers rely on virtual dispatch

refusal
Targetsymbol M:Shop.Order.ToString
Refusespolymorphic-method; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public string Number { get; set; }
6
7 public override string ToString() => Number;
8 }
9 }

#pass-instance

The method takes the instance as its first parameter; members it used are reached through it, and every call passes the instance

success
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
pass"instance"
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 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
55 private 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 order, string prefix)
1111 {
1212 // The total is shown without rounding.
13− return prefix + _total + " " + this.Currency;
13+ return prefix + order._total + " " + order.Currency;
1414 }
1515
16− public string Summary() => Describe("Order: ");
16+ public string Summary() => Describe(this, "Order: ");
1717
1818 public void Add(decimal price) => _total += price;
1919 }
2020 }

#pass-parameters

Each instance member the method reads becomes a parameter, in order of first use, and every call passes those members

success
Targetsymbol M:Shop.Order.Describe(System.String)
Arguments
pass"parameters"
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.Total, order.Currency, "Paid: ");
66 }
77 }
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
55 public decimal Total { get; set; }
66
77 public string Currency { get; set; }
88
9− public string Describe(string prefix)
9+ public static string Describe(decimal total, string currency, string prefix)
1010 {
11− return prefix + Total + " " + Currency + " (" + Total + ")";
11+ return prefix + total + " " + currency + " (" + total + ")";
1212 }
1313
14− public string Summary() => Describe("Order: ");
14+ public string Summary() => Describe(Total, Currency, "Order: ");
1515 }
1616 }