RefactorMCP catalog

Use Named Arguments

Names the arguments of one call after the parameters they are passed for.

Arguments

None. The target is a caret anywhere in the call: on the method's name, in its argument list, or on new for an object creation. With calls nested inside each other, the innermost call around the caret is the one changed.

Precondition

  • The caret is inside a method call, an object creation or a constructor initializer that binds to a single method.
  • The call has at least one argument that is not named yet and is not passed to a params array.
  • With the names written out, the call still binds to the same method. Named arguments can make another overload applicable, which would make the call ambiguous or change which method it calls.
  • The result compiles.

Transformation

  • Each positional argument gets name: in front, where name is the parameter it is passed for.
  • Arguments already named are left as they are, in the order written.
  • Arguments passed to a params array in expanded form stay positional, since they cannot be named; the named arguments before them are in position, which C# allows.
  • The receiver of an extension method called in reduced form is not an argument and is left alone.

Preserved

  • Which method the call binds to, and the value passed for each parameter. Arguments stay in the order they were written, so they are evaluated in the same order.
  • Layout, and comments around each argument. A comment before an argument stays before it, ahead of the new name.

Limitations

  • Only the one call at the caret changes; there is no option to name the arguments of every call of a method.
  • Indexer arguments and attribute arguments are not covered.

Error codes

CodeMeaning
no-call-at-caretthe caret is not inside a call
no-argumentsthe call has no arguments
already-namedevery argument that can be named already is
changes-overloadwith names, the call would bind to another method or none

Cases

·

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

#all-positional

Names every argument of the call at the caret

success
TargetShipping.cs, the /*^*/ caret
Shipping.csmodified
11 namespace Shop;
22
33 public class Shipping
44 {
55 public decimal Quote(decimal weight, string region, bool express)
66 {
77 return express ? weight * 2m : weight;
88 }
99
1010 public decimal Parcel()
1111 {
12− return /*^*/Quote(1.5m, "EU", true);
12+ return Quote(weight: 1.5m, region: "EU", express: true);
1313 }
1414 }

#already-named-rejected

Refuses a call whose arguments are all named already

refusal
TargetShipping.cs, the /*^*/ caret
Refusesalready-named; every file is left unchanged
Shipping.csinput
1 namespace Shop;
2
3 public class Shipping
4 {
5 public decimal Quote(decimal weight, string region)
6 {
7 return weight;
8 }
9
10 public decimal Parcel()
11 {
12 return /*^*/Quote(region: "EU", weight: 1.5m);
13 }
14 }

#changes-overload-rejected

Refuses when naming the arguments would make another overload a candidate, so the call would no longer bind to the same method

refusal
TargetGeometry.cs, the /*^*/ caret
Refuseschanges-overload; every file is left unchanged
Geometry.csinput
1 namespace Shop;
2
3 public class Geometry
4 {
5 public int Area(int width, long height)
6 {
7 return 1;
8 }
9
10 public int Area(long height, int width)
11 {
12 return 2;
13 }
14
15 public int Use()
16 {
17 return /*^*/Area(3, 4L);
18 }
19 }

#constructor-in-another-file

Names the arguments of an object creation, using the constructor declared in another file

success
TargetPacking.cs, the /*^*/ caret
Packing.csmodified
11 namespace Shop;
22
33 public class Packing
44 {
55 public Parcel Wrap()
66 {
7− return /*^*/new Parcel(2m, "fragile");
7+ return new Parcel(weight: 2m, label: "fragile");
88 }
99 }
Parcel.csunchanged
11 namespace Shop;
22
33 public class Parcel
44 {
55 public Parcel(decimal weight, string label)
66 {
77 Weight = weight;
88 Label = label;
99 }
1010
1111 public decimal Weight { get; }
1212
1313 public string Label { get; }
1414 }

#extension-method

Names the arguments of an extension method called in reduced form, skipping the receiver

success
TargetTextExtensions.cs, the /*^*/ caret
TextExtensions.csmodified
11 namespace Shop;
22
33 public static class TextExtensions
44 {
55 public static string Truncate(this string text, int length, string ellipsis)
66 {
77 return text.Length <= length ? text : text.Substring(0, length) + ellipsis;
88 }
99 }
1010
1111 public class Labels
1212 {
1313 public string Short(string name)
1414 {
15− return name./*^*/Truncate(10, "...");
15+ return name.Truncate(length: 10, ellipsis: "...");
1616 }
1717 }

#innermost-call

With the caret inside a nested call, names the arguments of that call only

success
TargetShipping.cs, the /*^*/ caret
Shipping.csmodified
11 namespace Shop;
22
33 public class Shipping
44 {
55 public decimal Quote(decimal weight, string region)
66 {
77 return weight;
88 }
99
1010 public decimal Weight(int items, decimal each)
1111 {
1212 return items * each;
1313 }
1414
1515 public decimal Parcel()
1616 {
17− return Quote(Weight(3, /*^*/0.5m), "EU");
17+ return Quote(Weight(items: 3, each: 0.5m), "EU");
1818 }
1919 }

#no-arguments-rejected

Refuses a call with no arguments to name

refusal
TargetClock.cs, the /*^*/ caret
Refusesno-arguments; every file is left unchanged
Clock.csinput
1 namespace Shop;
2
3 public class Clock
4 {
5 public int Hour()
6 {
7 return 9;
8 }
9
10 public int Next()
11 {
12 return /*^*/Hour() + 1;
13 }
14 }

#no-call-at-caret-rejected

Refuses when the caret is not in a call

refusal
TargetShipping.cs, the /*^*/ caret
Refusesno-call-at-caret; every file is left unchanged
Shipping.csinput
1 namespace Shop;
2
3 public class Shipping
4 {
5 public decimal Quote(decimal weight, string region, bool express)
6 {
7 return /*^*/express ? weight * 2m : weight;
8 }
9 }

#params-array

Arguments passed to a params array in expanded form stay positional

success
TargetLogger.cs, the /*^*/ caret
Logger.csmodified
11 namespace Shop;
22
33 public class Logger
44 {
55 public string Log(string format, params object[] values)
66 {
77 return string.Format(format, values);
88 }
99
1010 public string Run()
1111 {
12− return /*^*/Log("{0} {1}", 1, 2);
12+ return Log(format: "{0} {1}", 1, 2);
1313 }
1414 }

#preserves-trivia

Names arguments laid out one per line, keeping indentation and comments

success
TargetShipping.cs, the /*^*/ caret
Shipping.csmodified
11 namespace Shop;
22
33 public class Shipping
44 {
55 public decimal Quote(decimal weight, string region, bool express)
66 {
77 return express ? weight * 2m : weight;
88 }
99
1010 public decimal Parcel()
1111 {
1212 // next-day delivery
13− return /*^*/Quote(
14− 1.5m, // kilos
15− /* zone */ "EU",
16− true);
13+ return Quote(
14+ weight: 1.5m, // kilos
15+ /* zone */ region: "EU",
16+ express: true);
1717 }
1818 }

#some-already-named

Names the positional arguments and leaves named ones, in their written order

success
TargetShipping.cs, the /*^*/ caret
Shipping.csmodified
11 namespace Shop;
22
33 public class Shipping
44 {
55 public decimal Quote(decimal weight, string region, bool express)
66 {
77 return express ? weight * 2m : weight;
88 }
99
1010 public decimal Parcel()
1111 {
12− return Quote/*^*/(1.5m, express: true, region: "EU");
12+ return Quote(weight: 1.5m, express: true, region: "EU");
1313 }
1414 }