RefactorMCP catalog

Introduce Parameter

Turns an expression in a method body into a new parameter. The expression is replaced by the parameter, and every call passes the expression instead, computed from that call's own arguments.

Arguments

ArgumentMeaning
namethe name of the new parameter

The target is a selection covering exactly one expression inside a method or constructor body.

Precondition

  • The selection is a whole expression, give or take surrounding whitespace.
  • The expression reads nothing a caller cannot see: no locals, no lambda or local function parameters, no this, and no instance members of the containing type. It may read the method's parameters, constants, static members and members of other objects.
  • The expression's type does not use one of the method's type parameters.
  • The name is not already used in the method by a parameter or local, nor by a member the body could mean by it.
  • The result compiles.

Transformation

  • The selected occurrence of the expression becomes a reference to the new parameter. Other equal expressions are left alone.
  • The parameter is declared with the expression's type, after the existing required parameters and before any optional or params parameters.
  • Every call passes the expression, with each parameter it read replaced by the argument that call passes for it, parenthesised where precedence needs it. A parameter the call leaves to its default is replaced by the default. An extension method called in reduced form uses its receiver.
  • The signature change is carried out as by Change Signature, so overrides, interface members and their calls change too.

Preserved

  • The value the method computes, as long as the expression gives the same value when evaluated at the call instead of in the body.
  • Comments and layout around the expression and the calls.

Limitations

  • The expression is evaluated before the call rather than where it was, so side effects and timing move with it; an expression reading a parameter the body assigns first would see the original value.
  • Names in the expression are copied as written, so a call in another namespace may need a using the expression's file had.
  • Calls can end up with expressions a person would simplify, such as "sam" ?? "anonymous" (nullable-expression).
  • Instance members are not passed through the call's receiver; the refactoring refuses instead.

Error codes

CodeMeaning
not-an-expressionthe selection is not exactly one expression
references-localthe expression reads a local, which callers cannot see
references-instance-memberthe expression reads this or an instance member of the type
references-type-parameterthe expression uses one of the method's type parameters
name-conflictthe name is already used in the method

Cases

·

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

#before-optional-parameter

Places the new parameter before an optional one, naming the argument after an omitted default

success
TargetBanner.cs, the /*[*/ … /*]*/ selection
Arguments
name"width"
Banner.csmodified
11 namespace Shop;
22
33 public class Banner
44 {
5− public string Render(string text, char border = '*')
5+ public string Render(string text, int width, char border = '*')
66 {
7− return text.PadRight(/*[*/40/*]*/, border);
7+ return text.PadRight(width, border);
88 }
99
1010 public string Title()
1111 {
12− return Render("Sale") + Render("Clearance", '#');
12+ return Render("Sale", 40) + Render("Clearance", 40, '#');
1313 }
1414 }

#expression-of-parameters-across-files

Turns an expression over the method's parameters into a parameter; calls in another file pass it with their own arguments substituted

success
TargetOrder.cs, the /*[*/ … /*]*/ selection
Arguments
name"total"
Checkout.csmodified
11 namespace Shop;
22
33 public class Checkout
44 {
55 public string Receipt(Order order, int count)
66 {
7− return order.Summary(count + 1, 2.5m);
7+ return order.Summary(count + 1, 2.5m, (count + 1) * 2.5m);
88 }
99 }
Order.csmodified
11 namespace Shop;
22
33 public class Order
44 {
5− public string Summary(int quantity, decimal unitPrice)
5+ public string Summary(int quantity, decimal unitPrice, decimal total)
66 {
7− return "Total: " + /*[*/quantity * unitPrice/*]*/;
7+ return "Total: " + total;
88 }
99 }

#literal-in-body

Turns a literal into a parameter and passes the literal at the call

success
TargetTax.cs, the /*[*/ … /*]*/ selection
Arguments
name"rate"
Tax.csmodified
11 namespace Shop;
22
33 public class Tax
44 {
5− public decimal Vat(decimal price)
5+ public decimal Vat(decimal price, decimal rate)
66 {
7− return price * /*[*/0.2m/*]*/;
7+ return price * rate;
88 }
99
1010 public decimal Gross(decimal price)
1111 {
12− return price + Vat(price);
12+ return price + Vat(price, 0.2m);
1313 }
1414 }

#name-conflict-rejected

Refuses a name that is already a parameter of the method

refusal
TargetTax.cs, the /*[*/ … /*]*/ selection
Arguments
name"price"
Refusesname-conflict mentioning “price”; every file is left unchanged
Tax.csinput
1 namespace Shop;
2
3 public class Tax
4 {
5 public decimal Vat(decimal price)
6 {
7 return price * /*[*/0.2m/*]*/;
8 }
9 }

#not-an-expression-rejected

Refuses a selection that is a statement rather than an expression

refusal
TargetTax.cs, the /*[*/ … /*]*/ selection
Arguments
name"result"
Refusesnot-an-expression; every file is left unchanged
Tax.csinput
1 namespace Shop;
2
3 public class Tax
4 {
5 public decimal Vat(decimal price)
6 {
7 /*[*/return price * 0.2m;/*]*/
8 }
9 }

#nullable-expression

Introduces a parameter from a null-coalescing expression in a nullable context, with the non-nullable type of the expression

success
TargetProfile.cs, the /*[*/ … /*]*/ selection
Arguments
name"label"
Projectnullable enable
Profile.csmodified
11 namespace Shop;
22
33 public class Profile
44 {
5− public string Display(string? nickname)
5+ public string Display(string? nickname, string label)
66 {
7− return /*[*/nickname ?? "anonymous"/*]*/;
7+ return label;
88 }
99
1010 public string Card(string? name)
1111 {
12− return Display("sam") + Display(name);
12+ return Display("sam", "sam" ?? "anonymous") + Display(name, name ?? "anonymous");
1313 }
1414 }

#overloads

Changes only the overload the selection is in, and only its calls

success
TargetRounding.cs, the /*[*/ … /*]*/ selection
Arguments
name"decimals"
Rounding.csmodified
11 using System;
22
33 namespace Shop;
44
55 public class Rounding
66 {
77 public double Round(double value)
88 {
99 return Math.Round(value, 2);
1010 }
1111
12− public decimal Round(decimal value)
12+ public decimal Round(decimal value, int decimals)
1313 {
14− return decimal.Round(value, /*[*/2/*]*/);
14+ return decimal.Round(value, decimals);
1515 }
1616
1717 public string Show()
1818 {
19− return Round(1.234m) + " " + Round(1.234);
19+ return Round(1.234m, 2) + " " + Round(1.234);
2020 }
2121 }

#preserves-comments

Comments beside the expression and above the call stay where they are

success
TargetShipping.cs, the /*[*/ … /*]*/ selection
Arguments
name"baseFee"
Shipping.csmodified
11 namespace Shop;
22
33 public class Shipping
44 {
5− public decimal Cost(decimal weight)
5+ public decimal Cost(decimal weight, decimal baseFee)
66 {
77 // flat fee plus a rate per kilo
8− var fee = /*[*/5m/*]*/; // standard fee
8+ var fee = baseFee; // standard fee
99
1010 return fee + weight * 2m;
1111 }
1212
1313 public decimal Parcel()
1414 {
1515 // a small parcel
16− return Cost(1.5m);
16+ return Cost(1.5m, 5m);
1717 }
1818 }

#references-instance-member-rejected

Refuses an expression that reads an instance field through this

refusal
TargetTax.cs, the /*[*/ … /*]*/ selection
Arguments
name"rate"
Refusesreferences-instance-member mentioning “_rate”; every file is left unchanged
Tax.csinput
1 namespace Shop;
2
3 public class Tax
4 {
5 private readonly decimal _rate = 0.2m;
6
7 public decimal Vat(decimal price)
8 {
9 return price * /*[*/_rate/*]*/;
10 }
11 }

#references-local-rejected

Refuses an expression that reads a local, which callers cannot see

refusal
TargetShipping.cs, the /*[*/ … /*]*/ selection
Arguments
name"total"
Refusesreferences-local mentioning “fee”; every file is left unchanged
Shipping.csinput
1 namespace Shop;
2
3 public class Shipping
4 {
5 public decimal Cost(decimal weight)
6 {
7 var fee = 5m;
8 return /*[*/fee + weight/*]*/;
9 }
10 }

#references-type-parameter-rejected

Refuses an expression whose type uses the method's type parameter, which calls cannot name

refusal
TargetBoxes.cs, the /*[*/ … /*]*/ selection
Arguments
name"items"
Refusesreferences-type-parameter; every file is left unchanged
Boxes.csinput
1 using System.Collections.Generic;
2
3 namespace Shop;
4
5 public class Boxes
6 {
7 public int Count<T>(T item)
8 {
9 return /*[*/new List<T> { item }/*]*/.Count;
10 }
11 }