RefactorMCP catalog

Move Type to Namespace

Changes the namespace a top-level type is declared in, leaving it in its file, and keeps every reference to it compiling across the solution.

Arguments

ArgumentMeaning
target.symbolthe type to move, such as T:Shop.Invoice
namespacethe namespace to move it to, such as Shop.Billing

Precondition

  • The type is top-level and declared in one place (not a partial type with several parts).
  • namespace is a valid dotted name and differs from the current namespace.
  • The new namespace has no type with the same name and arity.
  • The type's namespace declaration is not nested inside another namespace block. A file-scoped namespace holds no other type.

Transformation

  • When the type is the only member of its namespace declaration, the declaration is renamed, block or file-scoped. Otherwise the type moves to a new namespace block of its own after the old one, in the same file, with the comments above it. A type in the global namespace is wrapped in a new block.
  • A reference qualified by the old namespace, including global:: names, using aliases and using static, is requalified with the new namespace.
  • A file that uses the type by its simple name, from outside the new namespace and its children, gains a using for the new namespace.
  • The moved type gains a using for each namespace it relied on without one: its old namespace and that namespace's parents, for the types and extension methods it uses from there.
  • A using that only the moved type needed, and that is unnecessary after the move, is removed. Usings that were already unnecessary are left alone.
  • References in other projects of the solution are updated the same way.

Preserved

  • The meaning of every name in the solution.
  • The type's file, members, comments and layout.

Limitations

  • Usings declared inside the old namespace block are copied into a new block when the type is split out, even where the type does not need them.
  • References in documentation comments (cref) are not updated.

Error codes

CodeMeaning
namespace-unchangedthe type is already in the namespace
type-existsthe new namespace already has a type of that name
nested-typethe type is nested inside another type
invalid-namespacethe namespace is not a valid dotted name
partial-typethe type is partial and declared in several places
nested-namespace-blockthe type's namespace block is nested in another
shares-file-scoped-namespacethe type shares a file-scoped namespace with other types

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

References in a project that depends on the type's project are updated too

success
Targetsymbol T:Core.Money
Arguments
namespace"Core.Values"
ProjectsCore; App → Core
App/Till.csmodified
1−using Core;
1+using Core.Values;
22
33 namespace App
44 {
55 public class Till
66 {
77 public Money Total { get; set; }
88 }
99 }
Core/Money.csmodified
1−namespace Core
1+namespace Core.Values
22 {
33 public struct Money
44 {
55 public decimal Amount { get; set; }
66 }
77 }

#file-scoped

Changes a file-scoped namespace, and a file with no usings gains one

success
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop.Billing"
Checkout.csmodified
1+using Shop.Billing;
2+
13 namespace Shop;
24
35 public class Checkout
46 {
57 public Invoice Bill(decimal amount) => new Invoice { Amount = amount };
68 }
Invoice.csmodified
1−namespace Shop;
1+namespace Shop.Billing;
22
33 // Issued once an order ships.
44 public class Invoice
55 {
66 public decimal Amount { get; set; }
77 }

#generic-type

A generic type moves, and its constructed uses in other files follow

success
Targetsymbol T:Shop.Result`1
Arguments
namespace"Shop.Common"
Orders.csmodified
1+using Shop.Common;
2+
13 namespace Shop
24 {
35 public class Orders
46 {
57 public Result<int> Count() => new Result<int>(3);
68 }
79 }
Result.csmodified
1−namespace Shop
1+namespace Shop.Common
22 {
33 public class Result<T>
44 {
55 public Result(T value) => Value = value;
66
77 public T Value { get; }
88 }
99 }

#invalid-namespace-rejected

Refuses a namespace that is not a valid dotted name

refusal
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop.2024"
Refusesinvalid-namespace; every file is left unchanged
Invoice.csinput
1 namespace Shop
2 {
3 public class Invoice
4 {
5 }
6 }

#nested-type-rejected

Refuses a nested type, whose namespace is its container's

refusal
Targetsymbol T:Shop.Order.Line
Arguments
namespace"Shop.Lines"
Refusesnested-type; every file is left unchanged
Order.csinput
1 namespace Shop
2 {
3 public class Order
4 {
5 public class Line
6 {
7 }
8 }
9 }

#old-namespace-still-needed

The moved type imports its old namespace for the types it uses from there, and a file that imported the old namespace only for the moved type imports the new one instead

success
Targetsymbol T:Shop.Billing.Invoice
Arguments
namespace"Shop.Accounts"
Billing/Invoice.csmodified
11 using System.Collections.Generic;
2+using Shop.Billing;
23
3−namespace Shop.Billing
4+namespace Shop.Accounts
45 {
56 public class Invoice
67 {
78 public List<TaxLine> Taxes { get; } = new List<TaxLine>();
89
910 public decimal Net { get; set; }
1011 }
1112 }
Checkout.csmodified
1−using Shop.Billing;
1+using Shop.Accounts;
22
33 namespace Shop
44 {
55 public class Checkout
66 {
77 public Invoice Bill(decimal net) => new Invoice { Net = net };
88 }
99 }
Billing/TaxLine.csunchanged
11 namespace Shop.Billing
22 {
33 public class TaxLine
44 {
55 public decimal Amount { get; set; }
66 }
77 }

#only-type-in-block

Renames the namespace block of a file holding one type, and imports the new namespace where the type is used

success
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop.Billing"
Checkout.csmodified
11 using System.Collections.Generic;
2+using Shop.Billing;
23
34 namespace Shop
45 {
56 public class Checkout
67 {
78 public List<Invoice> Issued { get; } = new List<Invoice>();
89
910 public Invoice Bill(Customer customer, decimal amount)
1011 {
1112 var invoice = new Invoice { Customer = customer, Amount = amount };
1213 Issued.Add(invoice);
1314 return invoice;
1415 }
1516 }
1617 }
Invoice.csmodified
1−namespace Shop
1+namespace Shop.Billing
22 {
33 public class Invoice
44 {
55 public Customer Customer { get; set; }
66
77 public decimal Amount { get; set; }
88 }
99 }
Customer.csunchanged
11 namespace Shop
22 {
33 public class Customer
44 {
55 public string Name { get; set; }
66 }
77 }

#qualified-references

Fully qualified references, a using alias and a using static are rewritten to the new namespace

success
Targetsymbol T:Shop.Billing.Tax
Arguments
namespace"Shop.Accounts"
Pricing.csmodified
1−using static Shop.Billing.Tax;
2−using Levy = Shop.Billing.Tax;
1+using static Shop.Accounts.Tax;
2+using Levy = Shop.Accounts.Tax;
33
44 namespace Shop
55 {
66 public class Pricing
77 {
8− public decimal Gross(decimal net) => net + Shop.Billing.Tax.On(net);
8+ public decimal Gross(decimal net) => net + Shop.Accounts.Tax.On(net);
99
1010 public decimal Rated(decimal net) => net * Rate;
1111
1212 public decimal Levied(decimal net) => Levy.On(net);
1313
14− public decimal Global(decimal net) => global::Shop.Billing.Tax.On(net);
14+ public decimal Global(decimal net) => global::Shop.Accounts.Tax.On(net);
1515 }
1616 }
Tax.csmodified
1−namespace Shop.Billing
1+namespace Shop.Accounts
22 {
33 public static class Tax
44 {
55 public const decimal Rate = 0.2m;
66
77 public static decimal On(decimal amount) => amount * Rate;
88 }
99 }

#shares-namespace-block

A type sharing its namespace block moves to a new block of its own after it, in the same file

success
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop.Billing"
Model.csmodified
1+using Shop.Billing;
2+
13 namespace Shop
24 {
35 public class Order
46 {
57 public Invoice Invoice { get; set; }
68 }
79
10+ public class Customer
11+ {
12+ public string Name { get; set; }
13+ }
14+}
15+
16+namespace Shop.Billing
17+{
818 /// <summary>What the customer is asked to pay.</summary>
919 public class Invoice
1020 {
1121 public decimal Amount { get; set; }
1222 }
13−
14− public class Customer
15− {
16− public string Name { get; set; }
17− }
1823 }

#type-exists-rejected

Refuses when the new namespace already has a type with the same name

refusal
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop.Billing"
Refusestype-exists; every file is left unchanged
Billing/Invoice.csinput
1 namespace Shop.Billing
2 {
3 public class Invoice
4 {
5 }
6 }
Invoice.csinput
1 namespace Shop
2 {
3 public class Invoice
4 {
5 }
6 }

#unchanged-rejected

Refuses to move a type to the namespace it is already in

refusal
Targetsymbol T:Shop.Invoice
Arguments
namespace"Shop"
Refusesnamespace-unchanged; every file is left unchanged
Invoice.csinput
1 namespace Shop
2 {
3 public class Invoice
4 {
5 }
6 }