RefactorMCP catalog

Redirect Calls with Constant Argument

Makes calls that pass a constant for a parameter call, directly, the method the original runs for that value. When SetValue(string name, int value) calls SetHeight(value) for "height", box.SetValue("height", 10) becomes box.SetHeight(10). Replace Parameter with Explicit Methods uses it once the branches have methods of their own.

Target

The method whose calls are redirected, by symbol, with:

ArgumentMeaning
parameterthe parameter the calls pass the constant for
valuethe constant, written as C#, such as "\"height\"" or "Zone.Europe"
methodthe method the original calls for that value

Precondition

  • The target is an ordinary, non-generic method with a block body. It is not virtual, abstract, an override or an interface implementation, so a call of it runs its body.
  • The value is a constant of the parameter's type.
  • The class has a method with the given name.
  • With the parameter holding the value, the method does nothing but call that method and return what it returns. Following the value from the start of the body through if statements that compare the parameter with constants using built-in == or !=, else branches and switch statements on the parameter with constant case labels, the statements reached are return M(...);, or for a void method M(...); return;, or M(...); as the method's last statement.
  • That call has no receiver or this, and passes some of the method's own parameters unchanged, in any order, each to a parameter of the same type and without ref, out, in or params. For a method returning a value, M returns the same type.
  • The redirected calls compile, so the method is accessible wherever a call is.

Transformation

  • Each call of the method, in any file or project, whose argument for the parameter is a constant equal to the value becomes a call of the named method on the same receiver, passing the call's arguments for the parameters the method passes on, in its order.
  • The arguments keep their comments. A named argument keeps its name, changed to the name of the parameter it now goes to.
  • Calls passing another value or a variable, and calls of other overloads, are left alone.

Preserved

  • What each redirected call does: it runs the call the method would have made for the value, with the same argument values.
  • The order arguments are evaluated in. A call is left alone when it would drop, repeat or reorder an argument that is anything but a constant, a local, a parameter, this or a field of this object, since that argument would no longer be evaluated as it was.

Limitations

  • A call that leaves out an optional argument, passes a params array in expanded form, or passes an argument by reference is left alone.
  • A call where the named method would bind to another method, such as one a derived class hides it with, is left alone.
  • A dispatch that compares the parameter in any other way, such as with Equals, patterns or combined conditions, ends the walk, and the method is refused as doing more than the call.
  • The original method stays, even when no call of it is left.

Error codes

CodeMeaning
not-a-methodthe target is not an ordinary method with a block body
polymorphic-methodthe method is virtual, an override or an interface implementation
generic-methodthe method is generic
unknown-parameterthe method has no parameter of that name
not-a-constantthe value is not a constant of the parameter's type
unknown-methodthe class has no method of the given name
does-more-than-callfor the value, the method does more than call the named method with its parameters
does-not-compilea redirected call would not compile, such as when the named method is not accessible there

Cases

·

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

#across-projects

A call in a project that references the method's project is redirected

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
ProjectsCore; App → Core
App/Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build(string dimension, int size)
66 {
77 var box = new Box();
8− box.SetValue("height", 10);
8+ box.SetHeight(10);
99 box.SetValue("width", size * 2);
1010 box.SetValue(dimension, size);
1111 return box;
1212 }
1313 }
1414 }
Core/Box.csunchanged
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetHeight(int value)
2626 {
2727 _height = value;
2828 }
2929
3030 public void SetWidth(int value)
3131 {
3232 _width = value;
3333 }
3434
3535 public int Area()
3636 {
3737 return _height * _width;
3838 }
3939 }
4040 }

#call-forms

A call inside the class with no receiver, a conditional call with named arguments, and a call on a new object are each redirected, keeping the receiver and the names of the arguments that remain

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Box.csmodified
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetHeight(int value)
2626 {
2727 _height = value;
2828 }
2929
3030 public void SetWidth(int value)
3131 {
3232 _width = value;
3333 }
3434
3535 public void Flatten()
3636 {
37− SetValue("height", 0);
37+ SetHeight(0);
3838 }
3939
4040 public int Area()
4141 {
4242 return _height * _width;
4343 }
4444 }
4545 }
Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build(Box box)
66 {
7− box?.SetValue(value: 3, name: "height");
8− new Box().SetValue("height", 4);
7+ box?.SetHeight(value: 3);
8+ new Box().SetHeight(4);
99 return box;
1010 }
1111 }
1212 }

#does-more-than-call-rejected

Refuses when the branch for the value does more than call the named method, since the call would skip the rest

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Refusesdoes-more-than-call; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 _width = value;
16 return;
17 }
18 if (name == "width")
19 {
20 SetWidth(value);
21 return;
22 }
23 throw new ArgumentException("Unknown dimension " + name);
24 }
25
26 public void SetHeight(int value)
27 {
28 _height = value;
29 }
30
31 public void SetWidth(int value)
32 {
33 _width = value;
34 }
35
36 public int Area()
37 {
38 return _height * _width;
39 }
40 }
41 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#expression-bodied-rejected

Refuses a method without a block body, which has no dispatch to follow

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Refusesnot-a-method; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value) => SetHeight(name == "height" ? value : throw new ArgumentException(name));
11
12 public void SetHeight(int value)
13 {
14 _height = value;
15 }
16
17 public void SetWidth(int value)
18 {
19 _width = value;
20 }
21
22 public int Area()
23 {
24 return _height * _width;
25 }
26 }
27 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#generic-class

A method of a generic class dispatching with a switch whose sections break; a call on a constructed type calls the named method on it

success
Targetsymbol M:Config.Setting`1.Set(System.String,`0)
Arguments
parameter"key"
value"\"name\""
method"SetName"
Profile.csmodified
11 namespace Config
22 {
33 public class Profile
44 {
55 public Setting<string> Create()
66 {
77 var setting = new Setting<string>();
8− setting.Set("name", "Ada");
8+ setting.SetName("Ada");
99 setting.Set("title", "Countess");
1010 return setting;
1111 }
1212 }
1313 }
Setting.csunchanged
11 namespace Config
22 {
33 public class Setting<T>
44 {
55 public T Name { get; private set; }
66
77 public T Title { get; private set; }
88
99 public void Set(string key, T value)
1010 {
1111 switch (key)
1212 {
1313 case "name":
1414 SetName(value);
1515 break;
1616 case "title":
1717 SetTitle(value);
1818 break;
1919 }
2020 }
2121
2222 public void SetName(T value)
2323 {
2424 Name = value;
2525 }
2626
2727 public void SetTitle(T value)
2828 {
2929 Title = value;
3030 }
3131 }
3232 }

#generic-method-rejected

Refuses a generic method, whose type arguments the named method would not take

refusal
Targetsymbol M:Shapes.Box.SetValue``1(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Refusesgeneric-method; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue<T>(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 public void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build()
6 {
7 var box = new Box();
8 box.SetValue<string>("height", 10);
9 return box;
10 }
11 }
12 }

#if-chain

A call in another file passing "height" calls SetHeight, which the if chain runs for that value; calls passing another value or a variable are left alone

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build(string dimension, int size)
66 {
77 var box = new Box();
8− box.SetValue("height", 10);
8+ box.SetHeight(10);
99 box.SetValue("width", size * 2);
1010 box.SetValue(dimension, size);
1111 return box;
1212 }
1313 }
1414 }
Box.csunchanged
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetHeight(int value)
2626 {
2727 _height = value;
2828 }
2929
3030 public void SetWidth(int value)
3131 {
3232 _width = value;
3333 }
3434
3535 public int Area()
3636 {
3737 return _height * _width;
3838 }
3939 }
4040 }

#inaccessible-method-rejected

Refuses when a redirected call could not reach the named method, which is private

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Refusesdoes-not-compile; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 private void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#not-a-constant-rejected

Refuses a value that is not a constant, which calls could not be matched against

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"value.ToString()"
method"SetHeight"
Refusesnot-a-constant; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 public void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#nullable-parameter

With nullable reference types enabled, a nullable string parameter is matched by its constant, and a call passing null is left alone

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Projectnullable enable
Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build()
66 {
77 var box = new Box();
8− box.SetValue("height", 10);
8+ box.SetHeight(10);
99 box.SetValue(null, 2);
1010 return box;
1111 }
1212 }
1313 }
Box.csunchanged
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string? name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetHeight(int value)
2626 {
2727 _height = value;
2828 }
2929
3030 public void SetWidth(int value)
3131 {
3232 _width = value;
3333 }
3434
3535 public int Area()
3636 {
3737 return _height * _width;
3838 }
3939 }
4040 }

#overloads

Only calls of the targeted overload are redirected; a call binding to the overload taking a double keeps calling it

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build()
66 {
77 var box = new Box();
8− box.SetValue("height", 10);
8+ box.SetHeight(10);
99 box.SetValue("height", 10.5);
1010 return box;
1111 }
1212 }
1313 }
Box.csunchanged
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetValue(string name, double value)
2626 {
2727 SetValue(name, (int)Math.Round(value));
2828 }
2929
3030 public void SetHeight(int value)
3131 {
3232 _height = value;
3333 }
3434
3535 public void SetWidth(int value)
3636 {
3737 _width = value;
3838 }
3939
4040 public int Area()
4141 {
4242 return _height * _width;
4343 }
4444 }
4545 }

#preserves-comments

Comments above the call, on its arguments and after it stay where they were

success
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Builder.csmodified
11 namespace Shapes
22 {
33 public class Builder
44 {
55 public Box Build()
66 {
77 var box = new Box();
88 // The height comes first.
9− box.SetValue("height", 10 /* fixed */); // tall
9+ box.SetHeight(10 /* fixed */); // tall
1010 return box;
1111 }
1212 }
1313 }
Box.csunchanged
11 using System;
22
33 namespace Shapes
44 {
55 public class Box
66 {
77 private int _height;
88 private int _width;
99
1010 public void SetValue(string name, int value)
1111 {
1212 if (name == "height")
1313 {
1414 SetHeight(value);
1515 return;
1616 }
1717 if (name == "width")
1818 {
1919 SetWidth(value);
2020 return;
2121 }
2222 throw new ArgumentException("Unknown dimension " + name);
2323 }
2424
2525 public void SetHeight(int value)
2626 {
2727 _height = value;
2828 }
2929
3030 public void SetWidth(int value)
3131 {
3232 _width = value;
3333 }
3434
3535 public int Area()
3636 {
3737 return _height * _width;
3838 }
3939 }
4040 }

#side-effects-kept

The branch of an else-if chain calls a method taking none of the parameters; a call whose other argument is a variable drops it, and a call whose other argument has side effects keeps calling the original method

success
Targetsymbol M:Tally.Counter.Apply(System.String,System.Int32)
Arguments
parameter"mode"
value"\"reset\""
method"Reset"
Counter.csmodified
11 namespace Tally
22 {
33 public class Counter
44 {
55 private int _total;
66
77 public void Apply(string mode, int amount)
88 {
99 if (mode == "reset")
1010 Reset();
1111 else if (mode == "add")
1212 Add(amount);
1313 }
1414
1515 public void Reset()
1616 {
1717 _total = 0;
1818 }
1919
2020 public void Add(int amount)
2121 {
2222 _total += amount;
2323 }
2424 }
2525
2626 public class Client
2727 {
2828 private int _calls;
2929
3030 public void Run(Counter counter, int start)
3131 {
32− counter.Apply("reset", start);
32+ counter.Reset();
3333 counter.Apply("reset", Next());
3434 counter.Apply("add", Next());
3535 }
3636
3737 private int Next() => ++_calls;
3838 }
3939 }

#switch-returning-value

A switch on an enum whose case returns the named method's result; a call passing the enum member as a constant, with no receiver, calls the method directly

success
Targetsymbol M:Billing.Rates.Postage(Billing.Zone,System.Decimal)
Arguments
parameter"zone"
value"Zone.Europe"
method"Europe"
Rates.csmodified
11 namespace Billing
22 {
33 public enum Zone
44 {
55 Domestic,
66 Europe,
77 World,
88 }
99
1010 public class Rates
1111 {
1212 public decimal Postage(Zone zone, decimal weight)
1313 {
1414 switch (zone)
1515 {
1616 case Zone.Domestic:
1717 return Domestic(weight);
1818 case Zone.Europe:
1919 return Europe(weight);
2020 default:
2121 return weight * 5m;
2222 }
2323 }
2424
2525 public decimal Domestic(decimal weight) => weight * 1m;
2626
2727 public decimal Europe(decimal weight) => weight * 2m;
2828
2929 public decimal Quote(decimal weight, Zone zone)
3030 {
31− return Postage(Zone.Europe, weight) + Postage(zone, weight) + Postage(Zone.World, weight);
31+ return Europe(weight) + Postage(zone, weight) + Postage(Zone.World, weight);
3232 }
3333 }
3434 }

#unknown-method-rejected

Refuses a method name the class has no method for

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetDepth"
Refusesunknown-method mentioning “'SetDepth'”; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 public void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#unknown-parameter-rejected

Refuses a parameter the method does not have

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"dimension"
value"\"height\""
method"SetHeight"
Refusesunknown-parameter mentioning “'dimension'”; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 public void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }

#virtual-method-rejected

Refuses a virtual method, since an override may do something else for the value

refusal
Targetsymbol M:Shapes.Box.SetValue(System.String,System.Int32)
Arguments
parameter"name"
value"\"height\""
method"SetHeight"
Refusespolymorphic-method; every file is left unchanged
Box.csinput
1 using System;
2
3 namespace Shapes
4 {
5 public class Box
6 {
7 private int _height;
8 private int _width;
9
10 public virtual void SetValue(string name, int value)
11 {
12 if (name == "height")
13 {
14 SetHeight(value);
15 return;
16 }
17 if (name == "width")
18 {
19 SetWidth(value);
20 return;
21 }
22 throw new ArgumentException("Unknown dimension " + name);
23 }
24
25 public void SetHeight(int value)
26 {
27 _height = value;
28 }
29
30 public void SetWidth(int value)
31 {
32 _width = value;
33 }
34
35 public int Area()
36 {
37 return _height * _width;
38 }
39 }
40 }
Builder.csinput
1 namespace Shapes
2 {
3 public class Builder
4 {
5 public Box Build(string dimension, int size)
6 {
7 var box = new Box();
8 box.SetValue("height", 10);
9 box.SetValue("width", size * 2);
10 box.SetValue(dimension, size);
11 return box;
12 }
13 }
14 }