RefactorMCP catalog

Convert to Extension Method

Makes a method callable as an extension method on the type of its first parameter.

Arguments

ArgumentMeaning
target.symbolthe method to convert, such as M:Shop.Text.Shout(System.String)
tooptional, for an instance method: the static class to put the extension in (default <Type>Extensions)

Precondition

For a static method, the usual case:

  • It is not already an extension method.
  • It is declared in a static, non-generic, top-level class.
  • It has at least one parameter, and the first is not out, params or a pointer.

For an instance method, no precondition beyond being an ordinary method of a class.

Transformation

  • A static method gains this on its first parameter. Each call written as Text.Shout(value, ...) or, inside the class, Shout(value, ...) becomes value.Shout(...), with the receiver parenthesized where precedence needs it. Explicit type arguments are kept.
  • A call keeps its static form when the extension form would not bind the same way: its first argument is a null or default literal, is named or passed by reference, converts to the parameter only by a user-defined or numeric conversion, or the calling file does not see the class's namespace.
  • An instance method moves to a static class beside its type, as an extension method whose first parameter is the instance, with uses of the instance's members qualified by that parameter. The original method stays, delegating to the extension, so callers are unchanged.

Preserved

  • The behaviour of every call.
  • Nullable annotations on the first parameter.

Limitations

  • For an instance method, members the method uses must be accessible from the new static class; private members are not made accessible.
  • For an instance method the extension parameter is named after the type in camel case and typed without type arguments, so generic types are not supported.

Error codes

CodeMeaning
already-extensionthe method is already an extension method
not-static-classthe static method's class is not static
nested-or-generic-classthe static method's class is nested or generic
no-parametersthe method has no parameters
invalid-first-parameterthe first parameter is out, params or a pointer

Cases

·

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

#already-extension-rejected

Refuses a method that is already an extension method

refusal
Targetsymbol M:Shop.Text.Shout(System.String)
Refusesalready-extension; every file is left unchanged
Text.csinput
1 namespace Shop
2 {
3 public static class Text
4 {
5 public static string Shout(this string text) => text.ToUpperInvariant();
6 }
7 }

#calls-kept-static

Calls whose first argument would not bind as a receiver, or whose file cannot see the extension, keep the static form

success
Targetsymbol M:Shop.Numbers.Twice(System.Int64)
Ledger.csmodified
11 namespace Shop
22 {
33 public class Ledger
44 {
55 public long Balance { get; set; }
66
7− public long Doubled() => Numbers.Twice(Balance);
7+ public long Doubled() => Balance.Twice();
88
99 public long FromInt(int small) => Numbers.Twice(small);
1010
1111 public long Literal() => Numbers.Twice(21);
1212 }
1313 }
Numbers.csmodified
11 namespace Shop
22 {
33 public static class Numbers
44 {
5− public static long Twice(long value) => value * 2;
5+ public static long Twice(this long value) => value * 2;
66 }
77 }
Reports/Summary.csunchanged
11 namespace Reports
22 {
33 public class Summary
44 {
55 public long Total(long value) => Shop.Numbers.Twice(value);
66 }
77 }

#generic-method

A generic method becomes a generic extension method, and inferred calls stay inferred

success
Targetsymbol M:Shop.Sequences.FirstOr``1(System.Collections.Generic.IEnumerable{``0},``0)
Basket.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop
44 {
55 public class Basket
66 {
77 private readonly List<int> _quantities = new List<int>();
88
9− public int First() => Sequences.FirstOr(_quantities, 0);
9+ public int First() => _quantities.FirstOr(0);
1010
11− public string FirstName(List<string> names) => Sequences.FirstOr<string>(names, "none");
11+ public string FirstName(List<string> names) => names.FirstOr<string>("none");
1212 }
1313 }
Sequences.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop
44 {
55 public static class Sequences
66 {
7− public static T FirstOr<T>(IEnumerable<T> items, T fallback)
7+ public static T FirstOr<T>(this IEnumerable<T> items, T fallback)
88 {
99 foreach (var item in items)
1010 return item;
1111 return fallback;
1212 }
1313 }
1414 }

#instance-method

An instance method becomes an extension method in a new static class beside its type, and the original delegates to it so callers are unchanged

success
Targetsymbol M:Shop.Order.Describe
Order.csmodified
11 namespace Shop
22 {
33 public class Order
44 {
55 public string Number { get; set; }
66
77 public decimal Total { get; set; }
88
99 public string Describe()
1010 {
11− return Number + ": " + Total;
11+ return OrderExtensions.Describe(this);
12+ }
13+ }
14+
15+ public static class OrderExtensions
16+ {
17+ public static string Describe(this Order order)
18+ {
19+ return order.Number + ": " + order.Total;
1220 }
1321 }
1422 }

#no-parameters-rejected

Refuses a method without parameters, which has nothing to extend

refusal
Targetsymbol M:Shop.Clock.Today
Refusesno-parameters; every file is left unchanged
Clock.csinput
1 namespace Shop
2 {
3 public static class Clock
4 {
5 public static string Today() => "Monday";
6 }
7 }

#not-static-class-rejected

Refuses a static method of a class that is not static, where extension methods cannot live

refusal
Targetsymbol M:Shop.Order.Parse(System.String)
Refusesnot-static-class; 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 static Order Parse(string text) => new Order { Number = text };
8 }
9 }

#nullable

Nullable annotations on the first parameter are kept, and a null argument keeps the static call

success
Targetsymbol M:Shop.Text.OrEmpty(System.String)
Projectnullable enable
Label.csmodified
11 namespace Shop
22 {
33 public class Label
44 {
55 public string? Caption { get; set; }
66
7− public string Show() => Text.OrEmpty(Caption);
7+ public string Show() => Caption.OrEmpty();
88
99 public string Blank() => Text.OrEmpty(null);
1010 }
1111 }
Text.csmodified
11 namespace Shop
22 {
33 public static class Text
44 {
5− public static string OrEmpty(string? text) => text ?? "";
5+ public static string OrEmpty(this string? text) => text ?? "";
66 }
77 }

#static-helper

A static method of a static class becomes an extension method on its first parameter, and calls in other files take the extension form

success
Targetsymbol M:Shop.Text.Shout(System.String)
Checkout.csmodified
11 namespace Shop
22 {
33 public class Checkout
44 {
5− public string Welcome(Customer customer) => Text.Shout(customer.Name);
5+ public string Welcome(Customer customer) => customer.Name.Shout();
66 }
77
88 public class Customer
99 {
1010 public string Name { get; set; }
1111 }
1212 }
Text.csmodified
11 namespace Shop
22 {
33 public static class Text
44 {
55 /// <summary>Upper-cases and exclaims.</summary>
6− public static string Shout(string text) => text.ToUpperInvariant() + "!";
6+ public static string Shout(this string text) => text.ToUpperInvariant() + "!";
77
8− public static string Greet(string name) => Shout("hello " + name);
8+ public static string Greet(string name) => ("hello " + name).Shout();
99 }
1010 }