RefactorMCP catalog

Merge Partial Declarations

Moves the members of every part of a partial type into one of its declarations, deleting the parts and any file they leave empty.

Arguments

None. The target is the type, by symbol, with file naming the file whose declaration receives the members. Without file, the first declaration of the type receives them.

Precondition

  • The type has at least two declarations.
  • The using directives the moved members need can be added to the receiving file without making a name there ambiguous or redefining an alias.

Transformation

  • The receiving declaration keeps its members and gains the members of each other part after them, parts in the same file first, then the other files in path order, each part's members in their order.
  • Modifiers stated on any part are combined: an accessibility missing from the receiving declaration is added first, other modifiers such as sealed, static or abstract before partial.
  • Base types stated on any part are combined, a base class first.
  • Constraint clauses and attributes stated on another part are brought over.
  • Comments above another part go before its first member, and the comments and directives before its closing brace, such as #endregion, go before the receiving declaration's closing brace. Moved members are reindented.
  • partial is dropped, unless the type declares a partial member, whose declaration and implementation must stay partial.
  • The using directives of another part's file that its members need are added to the receiving file.
  • A part is removed from its file. A file left without any type is deleted.

Preserved

  • The type's members, base types, modifiers, constraints and attributes.
  • What every name in the moved members binds to.

Limitations

  • Documentation comments on parts other than the receiving one are dropped.
  • using directives inside a namespace block are not brought over.
  • Parts under different #if conditions are not detected.
  • When a moved member's name would be ambiguous in the receiving file the merge is refused rather than the name qualified.

Error codes

CodeMeaning
single-declarationthe type has only one declaration, so there is nothing to merge
conflicting-importsa using directive the moved members need would make a name ambiguous in the receiving file

Cases

·

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

#comments-and-regions

Keeps the comment above the other part and the region around its members, reindenting them for a block-scoped namespace

success
TargetReport.cs, symbol T:Shop.Report
Report.Printing.csdeleted
1−namespace Shop;
2−
3−// Printing is kept apart from the data.
4−public partial class Report
5−{
6− #region Printing
7−
8− /// <summary>The report as text.</summary>
9− public string Print() => Title.ToUpperInvariant();
10−
11− #endregion
12−}
Report.csmodified
11 namespace Shop
22 {
33 /// <summary>A printable report.</summary>
4− public partial class Report
4+ public class Report
55 {
66 // The heading printed on every page.
77 public string Title { get; set; } = "";
8+
9+ // Printing is kept apart from the data.
10+ #region Printing
11+
12+ /// <summary>The report as text.</summary>
13+ public string Print() => Title.ToUpperInvariant();
14+
15+ #endregion
816 }
917 }

#conflicting-imports-rejected

Refuses when the other part's using directive would make a name in the receiving file ambiguous

refusal
TargetInvoice.cs, symbol T:Shop.Invoice
Refusesconflicting-imports mentioning “Rate”; every file is left unchanged
Invoice.Legacy.csinput
1 using Shop.Legacy;
2
3 namespace Shop;
4
5 public partial class Invoice
6 {
7 public decimal LegacyTax(decimal net) => net * new Rate().Value;
8 }
Invoice.csinput
1 using Shop.Pricing;
2
3 namespace Shop;
4
5 public partial class Invoice
6 {
7 public decimal Tax(decimal net) => net * new Rate().Value;
8 }
Rates.csinput
1 namespace Shop.Pricing
2 {
3 public class Rate
4 {
5 public decimal Value => 0.2m;
6 }
7 }
8
9 namespace Shop.Legacy
10 {
11 public class Rate
12 {
13 public decimal Value => 0.175m;
14 }
15 }

#generic-with-constraint

Brings the constraint stated on the other part of a generic class

success
TargetCache.cs, symbol T:Shop.Cache`1
Cache.Store.csdeleted
1−namespace Shop;
2−
3−partial class Cache<T> where T : class
4−{
5− public void Store(string key, T value) => _items[key] = value;
6−
7− public T Find(string key) => _items.TryGetValue(key, out var value) ? value : null;
8−}
Cache.csmodified
11 using System.Collections.Generic;
22
33 namespace Shop;
44
5−public partial class Cache<T>
5+public class Cache<T> where T : class
66 {
77 private readonly Dictionary<string, T> _items = new();
88
99 public int Count => _items.Count;
10+
11+ public void Store(string key, T value) => _items[key] = value;
12+
13+ public T Find(string key) => _items.TryGetValue(key, out var value) ? value : null;
1014 }

#modifiers-base-types-and-usings

Combines the modifiers and base types of every part and brings the using directives the moved members need

success
TargetOrder.cs, symbol T:Shop.Order
Order.Validation.csdeleted
1−using System.Collections.Generic;
2−using System.Text;
3−using Shop.Rules;
4−
5−namespace Shop;
6−
7−sealed partial class Order : IValidatable
8−{
9− public IEnumerable<string> Validate()
10− {
11− yield return "ok";
12− }
13−}
Order.csmodified
11 using System;
2+using System.Collections.Generic;
3+using Shop.Rules;
24
35 namespace Shop;
46
5−public partial class Order
7+public sealed class Order : IValidatable
68 {
79 public DateTime Placed { get; init; }
10+
11+ public IEnumerable<string> Validate()
12+ {
13+ yield return "ok";
14+ }
815 }
Rules/IValidatable.csunchanged
11 using System.Collections.Generic;
22
33 namespace Shop.Rules;
44
55 public interface IValidatable
66 {
77 IEnumerable<string> Validate();
88 }

#partial-method-keeps-partial

Keeps the type partial when it declares a partial method, whose two halves end up together

success
TargetCustomer.cs, symbol T:Shop.Customer
Customer.Audit.csdeleted
1−namespace Shop;
2−
3−public partial class Customer
4−{
5− public int Renames { get; private set; }
6−
7− partial void OnRenamed()
8− {
9− Renames++;
10− }
11−}
Customer.csmodified
11 namespace Shop;
22
33 public partial class Customer
44 {
55 public string Name { get; private set; } = "";
66
77 public void Rename(string name)
88 {
99 Name = name;
1010 OnRenamed();
1111 }
1212
1313 partial void OnRenamed();
14+
15+ public int Renames { get; private set; }
16+
17+ partial void OnRenamed()
18+ {
19+ Renames++;
20+ }
1421 }

#parts-in-the-same-file-and-a-shared-file

Merges a second part in the same file and one in a file that keeps another type, which is not deleted

success
TargetOrder.cs, symbol T:Shop.Order
Order.csmodified
11 namespace Shop;
22
3−public partial class Order
3+public class Order
44 {
55 public int Quantity { get; set; }
6+
7+ public decimal Price { get; set; }
8+
9+ public decimal Total() => Quantity * Price;
610 }
7−
8−public partial class Order
9−{
10− public decimal Price { get; set; }
11−}
Pricing.csmodified
11 namespace Shop;
2−
3−public partial class Order
4−{
5− public decimal Total() => Quantity * Price;
6−}
72
83 public static class Pricing
94 {
105 public static decimal Discount(Order order) => order.Total() * 0.1m;
116 }

#single-declaration-rejected

Refuses a partial class that has only one declaration

refusal
Targetsymbol T:Shop.Invoice
Refusessingle-declaration; every file is left unchanged
Invoice.csinput
1 namespace Shop;
2
3 public partial class Invoice
4 {
5 public decimal Total { get; set; }
6 }

#two-files

Moves the members of the other part into Basket.cs, deletes the emptied file and drops partial

success
TargetBasket.cs, symbol T:Shop.Basket
Basket.Items.csdeleted
1−namespace Shop;
2−
3−partial class Basket
4−{
5− public void Add()
6− {
7− Count++;
8− }
9−
10− public void Clear()
11− {
12− Count = 0;
13− }
14−}
Basket.csmodified
11 namespace Shop;
22
3−public partial class Basket
3+public class Basket
44 {
55 public int Count { get; private set; }
6+
7+ public void Add()
8+ {
9+ Count++;
10+ }
11+
12+ public void Clear()
13+ {
14+ Count = 0;
15+ }
616 }