RefactorMCP catalog

Change Type

Changes the declared type of a local, parameter, field, property or method return value, typically to a base type or interface its uses need ("use base type where possible"). Introduce Interface for Dependency uses it after Extract Interface.

Arguments

ArgumentRequiredMeaning
toyesThe new type, as it would be written in the declaring file: IWriter, IEnumerable<Order>
parameternoWith a method target, the parameter to change instead of the return type

Targets:

  • a field, property or method return type by symbol: "target": { "symbol": "F:Shop.Report._writer" };
  • a parameter by its method's symbol and parameter: "target": { "symbol": "M:Shop.Report.Print(Shop.FileWriter)" }, "arguments": { "parameter": "writer", "to": "IWriter" };
  • a local by a caret on its name: "target": { "file": "Report.cs", "caret": "marker" }.

Precondition

  • to names a type the declaring file can see, or can see once its namespace is imported.
  • Everything still compiles: every value assigned or returned converts to the new type, and every use of the variable, or of the method's result, needs only what the new type offers. This covers callers in other files, overrides and interface implementations.
  • Every use still reaches the same code: a call passing the variable picks the same overload, and a member used through the new type is the one used before, or one the old member overrides or implicitly implements.

Transformation

  • The type is written in place. var is replaced by the new type.
  • A nullable declaration stays nullable: FileWriter? becomes IWriter?.
  • A variable declared alongside others gets a declaration of its own, after the original, with the new type.
  • The declaring file gains a using directive when the new type needs one, and a qualified name its usings already cover is shortened.

Preserved

  • Behaviour: the checks above refuse any change that would make a use reach different code.
  • Initializers, modifiers and comments of the declaration.

Limitations

  • The new type must be named; the most general type every use allows is not worked out.
  • Uses through operators, conversions and pattern matching are left to the compile check, so a user-defined conversion that differs between the two types is not detected.

Error codes

CodeMeaning
type-not-foundto names no type the file can see
parameter-not-foundthe method has no parameter of that name
incompatible-usecode using the variable or result would no longer compile
changes-overloada use would reach a different overload or member

Cases

·

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

#caller-needs-old-type

Refuses when a caller in another file needs the old return type

refusal
Targetsymbol M:Shop.Store.Pending
Arguments
to"IEnumerable<Order>"
Refusesincompatible-use mentioning “Dispatch.cs”; every file is left unchanged
Dispatch.csinput
1 namespace Shop
2 {
3 public static class Dispatch
4 {
5 public static int Count(Store store) => store.Pending().Count;
6 }
7 }
Store.csinput
1 using System.Collections.Generic;
2
3 namespace Shop
4 {
5 public class Order
6 {
7 public bool Shipped;
8 }
9
10 public class Store
11 {
12 private readonly List<Order> _orders = new List<Order>();
13
14 public List<Order> Pending() => _orders.FindAll(o => !o.Shipped);
15 }
16 }

#explicit-implementation-differs

Refuses when a member used through the new type is implemented by a different member than the one called today

refusal
Targetsymbol F:Shop.Report._writer
Arguments
to"IWriter"
Refuseschanges-overload mentioning “Write”; every file is left unchanged
Report.csinput
1 namespace Shop
2 {
3 public class Report
4 {
5 // Where the report goes.
6 private readonly FileWriter _writer = new FileWriter();
7
8 public void Print() => _writer.Write("report");
9 }
10 }
Writers.csinput
1 namespace Shop
2 {
3 public interface IWriter
4 {
5 void Write(string text);
6 }
7
8 public class FileWriter : IWriter
9 {
10 public void Write(string text)
11 {
12 }
13
14 void IWriter.Write(string text)
15 {
16 }
17 }
18 }

#field-to-interface

Declares a field as the interface its uses need rather than the concrete class

success
Targetsymbol F:Shop.Report._writer
Arguments
to"IWriter"
Report.csmodified
11 namespace Shop
22 {
33 public class Report
44 {
55 // Where the report goes.
6− private readonly FileWriter _writer = new FileWriter();
6+ private readonly IWriter _writer = new FileWriter();
77
88 public void Print() => _writer.Write("report");
99 }
1010 }
Writers.csunchanged
11 namespace Shop
22 {
33 public interface IWriter
44 {
55 void Write(string text);
66 }
77
88 public class FileWriter : IWriter
99 {
1010 public void Write(string text)
1111 {
1212 }
1313
1414 public void Flush()
1515 {
1616 }
1717 }
1818 }

#local-var-to-interface

Replaces var with the interface for a local picked by the caret

success
TargetReport.cs, the /*^*/ caret
Arguments
to"IWriter"
Report.csmodified
11 namespace Shop
22 {
33 public class Report
44 {
55 public void Print()
66 {
7− var /*^*/writer = new FileWriter();
7+ IWriter writer = new FileWriter();
88 writer.Write("report");
99 }
1010 }
1111 }
Writers.csunchanged
11 namespace Shop
22 {
33 public interface IWriter
44 {
55 void Write(string text);
66 }
77
88 public class FileWriter : IWriter
99 {
1010 public void Write(string text)
1111 {
1212 }
1313
1414 public void Flush()
1515 {
1616 }
1717 }
1818 }

#member-not-on-new-type

Refuses when the variable is used for a member the new type does not have

refusal
Targetsymbol F:Shop.Report._writer
Arguments
to"IWriter"
Refusesincompatible-use mentioning “Flush”; every file is left unchanged
Report.csinput
1 namespace Shop
2 {
3 public class Report
4 {
5 private readonly FileWriter _writer = new FileWriter();
6
7 public void Print()
8 {
9 _writer.Write("report");
10 _writer.Flush();
11 }
12 }
13 }
Writers.csinput
1 namespace Shop
2 {
3 public interface IWriter
4 {
5 void Write(string text);
6 }
7
8 public class FileWriter : IWriter
9 {
10 public void Write(string text)
11 {
12 }
13
14 public void Flush()
15 {
16 }
17 }
18 }

#one-of-several-fields

Splits a declaration of several fields and changes only the target

success
Targetsymbol F:Shop.Report._backup
Arguments
to"IWriter"
Report.csmodified
11 namespace Shop
22 {
33 public class Report
44 {
5− private FileWriter _main = new FileWriter(), _backup = new FileWriter();
5+ private FileWriter _main = new FileWriter();
6+ private IWriter _backup = new FileWriter();
67
78 public void Print()
89 {
910 _main.Write("report");
1011 _main.Flush();
1112 _backup.Write("report");
1213 }
1314 }
1415 }
Writers.csunchanged
11 namespace Shop
22 {
33 public interface IWriter
44 {
55 void Write(string text);
66 }
77
88 public class FileWriter : IWriter
99 {
1010 public void Write(string text)
1111 {
1212 }
1313
1414 public void Flush()
1515 {
1616 }
1717 }
1818 }

#overload-would-change

Refuses when a call passing the variable would pick a different overload

refusal
TargetReport.cs, the /*^*/ caret
Arguments
to"IWriter"
Refuseschanges-overload mentioning “Describe”; every file is left unchanged
Report.csinput
1 namespace Shop
2 {
3 public class Report
4 {
5 public string Print()
6 {
7 var /*^*/writer = new FileWriter();
8 return Describe(writer);
9 }
10
11 private static string Describe(FileWriter writer) => "file";
12
13 private static string Describe(IWriter writer) => "any";
14 }
15 }
Writers.csinput
1 namespace Shop
2 {
3 public interface IWriter
4 {
5 void Write(string text);
6 }
7
8 public class FileWriter : IWriter
9 {
10 public void Write(string text)
11 {
12 }
13
14 public void Flush()
15 {
16 }
17 }
18 }

#parameter-not-found

Refuses a parameter name the method does not have

refusal
Targetsymbol M:Shop.Report.Print(Shop.FileWriter)
Arguments
parameter"output"
to"IWriter"
Refusesparameter-not-found mentioning “output”; every file is left unchanged
Report.csinput
1 namespace Shop
2 {
3 public class Report
4 {
5 public void Print(FileWriter writer)
6 {
7 writer.Write("report");
8 }
9 }
10 }
Writers.csinput
1 namespace Shop
2 {
3 public interface IWriter
4 {
5 void Write(string text);
6 }
7
8 public class FileWriter : IWriter
9 {
10 public void Write(string text)
11 {
12 }
13
14 public void Flush()
15 {
16 }
17 }
18 }

#parameter-to-interface

Widens a parameter to an interface, leaving callers in another file that pass the class

success
Targetsymbol M:Shop.Report.Print(Shop.FileWriter)
Arguments
parameter"writer"
to"IWriter"
Report.csmodified
11 namespace Shop
22 {
33 public class Report
44 {
5− public void Print(FileWriter writer)
5+ public void Print(IWriter writer)
66 {
77 writer.Write("report");
88 }
99 }
1010 }
Program.csunchanged
11 namespace Shop
22 {
33 public static class Program
44 {
55 public static void Run() => new Report().Print(new FileWriter());
66 }
77 }
Writers.csunchanged
11 namespace Shop
22 {
33 public interface IWriter
44 {
55 void Write(string text);
66 }
77
88 public class FileWriter : IWriter
99 {
1010 public void Write(string text)
1111 {
1212 }
1313
1414 public void Flush()
1515 {
1616 }
1717 }
1818 }

#property-keeps-nullable

Keeps a property nullable when changing its type

success
Targetsymbol P:Shop.Report.Writer
Arguments
to"IWriter"
Projectnullable enable
Report.csmodified
11 namespace Shop
22 {
33 public class Report
44 {
5− public FileWriter? Writer { get; set; }
5+ public IWriter? Writer { get; set; }
66
77 public void Print() => Writer?.Write("report");
88 }
99 }
Writers.csunchanged
11 namespace Shop
22 {
33 public interface IWriter
44 {
55 void Write(string text);
66 }
77
88 public class FileWriter : IWriter
99 {
1010 public void Write(string text)
1111 {
1212 }
1313
1414 public void Flush()
1515 {
1616 }
1717 }
1818 }

#return-type-to-base

Returns a more general type when every caller only enumerates the result

success
Targetsymbol M:Shop.Store.Pending
Arguments
to"IEnumerable<Order>"
Store.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop
44 {
55 public class Order
66 {
77 public bool Shipped;
88 }
99
1010 public class Store
1111 {
1212 private readonly List<Order> _orders = new List<Order>();
1313
14− public List<Order> Pending() => _orders.FindAll(o => !o.Shipped);
14+ public IEnumerable<Order> Pending() => _orders.FindAll(o => !o.Shipped);
1515 }
1616 }
Dispatch.csunchanged
11 namespace Shop
22 {
33 public static class Dispatch
44 {
55 public static void ShipAll(Store store)
66 {
77 foreach (var order in store.Pending())
88 order.Shipped = true;
99 }
1010 }
1111 }

#type-from-another-namespace

Imports the namespace of the new type

success
Targetsymbol F:Shop.Reports.Report._log
Arguments
to"ILog"
Report.csmodified
11 using System;
2+using Shop.Logging;
23
34 namespace Shop.Reports
45 {
56 public class Report
67 {
7− private readonly Logging.ConsoleLog _log = new Logging.ConsoleLog();
8+ private readonly ILog _log = new Logging.ConsoleLog();
89
910 public void Print() => _log.Info(DateTime.Now.ToString());
1011 }
1112 }
Logging/Log.csunchanged
11 namespace Shop.Logging
22 {
33 public interface ILog
44 {
55 void Info(string message);
66 }
77
88 public class ConsoleLog : ILog
99 {
1010 public void Info(string message)
1111 {
1212 }
1313 }
1414 }
Program.csunchanged
11 namespace Shop
22 {
33 public static class Program
44 {
55 public static Reports.Report Create() => new Reports.Report();
66 }
77 }

#type-not-found

Refuses a type that does not exist

refusal
Targetsymbol F:Shop.Report._writer
Arguments
to"IPrinter"
Refusestype-not-found mentioning “IPrinter”; every file is left unchanged
Report.csinput
1 namespace Shop
2 {
3 public class Report
4 {
5 // Where the report goes.
6 private readonly FileWriter _writer = new FileWriter();
7
8 public void Print() => _writer.Write("report");
9 }
10 }
Writers.csinput
1 namespace Shop
2 {
3 public interface IWriter
4 {
5 void Write(string text);
6 }
7
8 public class FileWriter : IWriter
9 {
10 public void Write(string text)
11 {
12 }
13
14 public void Flush()
15 {
16 }
17 }
18 }