Removes a member that only forwards, through base, to the inherited member it hides or overrides, so its callers reach the inherited member directly. Replace Delegation with Inheritance uses it once a class's forwarding members reach their targets through base.
Arguments
None. The target is the member, by symbol: "target": { "symbol": "M:Staff.Employee.LastName" }, or for an indexer "target": { "symbol": "P:Shop.Stack.Item(System.Int32)" }.
Precondition
The member is a method, property or indexer whose whole body reaches the inherited member of the same name through base: a method whose body is base.M(a, b), passing its own parameters in order and by the same ref kind; a property whose getter reads base.P and whose setter, if any, assigns base.P = value; an indexer that does the same with base[i]. Expression and block bodies are both recognised. The inherited member has the same signature: the same return or property type, and parameters with the same names, types, modifiers and default values, so no call binds or behaves differently. The inherited member, and its setter where the member has one, is at least as accessible as the member. The member is not a new virtual member that subclasses override. The result compiles.
The member is removed with its comments and documentation. Inside the class, uses of the inherited member written with base become plain uses (base.LastName() becomes LastName(), base[i] becomes this[i]), unless a subclass overrides it, where base is needed to keep reaching the inherited implementation.
Preserved
Every call of the removed member, which now reaches the inherited member it forwarded to. Comments and blank lines around the other members.
Limitations
Generic methods and members with attributes or modifiers on their accessors are not recognised as forwarding. A member forwarding to a field rather than base is not removed; Replace Field Uses with Base turns such a field into the instance first.
Error codes
Code Meaning not-delegatingthe member does more than reach the inherited member of the same name through base signature-differsthe inherited member's signature differs, for example in a parameter's name or default value inherited-less-accessiblethe inherited member or its setter is less accessible than the member member-overriddenthe member is virtual and a subclass overrides it
Cases
Expand all · Collapse all
Diffs show each file from before to after .
/*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.
# base-use-kept-for-overridden-memberA use written with base stays when a subclass overrides the member, since without base it would reach the override
success
Target symbol M:Shop.Report.Title
Report.cs modified
1 1 namespace Shop 2 2 { 3 3 public class Document 4 4 { 5 5 public virtual string Title () => "Document" ; 6 6 } 7 7 8 8 public class Report : Document 9 9 { 10 − public override string Title () => base .Title (); 11 − 12 10 public string Print () => "# " + base .Title (); 13 11 } 14 12 15 13 public class AnnualReport : Report 16 14 { 17 15 public override string Title () => "Annual report" ; 18 16 } 19 17 }
View fixture on GitHub
# base-uses-simplifiedUses of the inherited member written with base, needed only while the removed member hid it, become plain uses
success
Target symbol M:Staff.Employee.LastName
Employee.cs modified
1 1 namespace Staff 2 2 { 3 3 public class Employee : Person 4 4 { 5 − public string LastName () 6 − { 7 − return base .LastName (); 8 − } 9 − 10 − public string Badge () => Greeting ("Employee" ) + " (" + base .LastName () + ")" ; 5 + public string Badge () => Greeting ("Employee" ) + " (" + LastName () + ")" ; 11 6 } 12 7 }
Payroll.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Payroll 4 4 { 5 5 public string Slip (Employee employee) 6 6 { 7 7 employee.Name = "Ann Lee" ; 8 8 return employee.LastName () + ": " + employee.Badge (); 9 9 } 10 10 } 11 11 }
Person.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Person 4 4 { 5 5 public string Name { get ; set ; } = "" ; 6 6 7 7 public string LastName () => Name .Split (" " )[^1 ]; 8 8 9 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 10 } 11 11 }
View fixture on GitHub
# forwards-to-another-member-rejectedRefuses a member that forwards to an inherited member of another name, which callers would not reach once it is gone
refusal
Target symbol M:Staff.Employee.Surname
Refuses not-delegating; every file is left unchanged
Employee.cs input
1 namespace Staff 2 { 3 public class Employee : Person 4 { 5 public string Surname () => base .LastName (); 6 } 7 }
Person.cs input
1 namespace Staff 2 { 3 public class Person 4 { 5 public string Name { get ; set ; } = "" ; 6 7 public string LastName () => Name .Split (" " )[^1 ]; 8 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 } 11 }
View fixture on GitHub
# genericA forwarding member of a generic class, in terms of its type parameter, is removed
success
Target symbol M:Shop.Registry`1.Add(`0)
Registry.cs modified
1 1 using System .Collections .Generic ;2 2 3 3 namespace Shop 4 4 { 5 5 public class Registry <T > : List <T > 6 6 { 7 − public new void Add (T item) => base .Add (item); 8 − 9 7 public void Register (T item) 10 8 { 11 9 if (!Contains (item)) 12 10 Add (item); 13 11 } 14 12 } 15 13 }
View fixture on GitHub
# indexerRemoves an indexer that only reaches the inherited indexer; element accesses written with base become accesses on this
success
Target symbol P:Shop.Stack.Item(System.Int32)
Stack.cs modified
1 1 using System .Collections .Generic ;2 2 3 3 namespace Shop 4 4 { 5 5 public class Stack : List <int > 6 6 { 7 − public new int this [int index] { get => base [index]; set => base [index] = value ; } 8 − 9 − public int Peek () => base [Count - 1 ]; 7 + public int Peek () => this [Count - 1 ]; 10 8 } 11 9 }
View fixture on GitHub
# inherited-less-accessible-rejectedRefuses when the inherited member is less accessible than the one forwarding to it
refusal
Target symbol M:Staff.Employee.Code
Refuses inherited-less-accessible; every file is left unchanged
Employee.cs input
1 namespace Staff 2 { 3 public class Employee : Person 4 { 5 public new string Code () => base .Code (); 6 } 7 }
Person.cs input
1 namespace Staff 2 { 3 public class Person 4 { 5 protected string Code () => "P-1" ; 6 } 7 }
View fixture on GitHub
# member-overridden-rejectedRefuses a virtual member a subclass overrides, which would be left overriding something else
refusal
Target symbol M:Staff.Employee.LastName
Refuses member-overridden mentioning “Manager”; every file is left unchanged
Employee.cs input
1 namespace Staff 2 { 3 public class Employee : Person 4 { 5 public new virtual string LastName () => base .LastName (); 6 } 7 8 public class Manager : Employee 9 { 10 public override string LastName () => "Manager " + base .LastName (); 11 } 12 }
Person.cs input
1 namespace Staff 2 { 3 public class Person 4 { 5 public string Name { get ; set ; } = "" ; 6 7 public string LastName () => Name .Split (" " )[^1 ]; 8 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 } 11 }
View fixture on GitHub
# methodRemoves a method that only calls the method it hides through base; callers now reach the inherited method
success
Target symbol M:Staff.Employee.LastName
Employee.cs modified
1 1 namespace Staff 2 2 { 3 3 public class Employee : Person 4 4 { 5 5 public decimal Salary { get ; set ; } 6 6 7 − public new string LastName () 8 − { 9 − return base .LastName (); 10 − } 11 − 12 7 public string Badge () => "Employee " + Name ; 13 8 } 14 9 }
Payroll.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Payroll 4 4 { 5 5 public string Slip (Employee employee) 6 6 { 7 7 employee.Name = "Ann Lee" ; 8 8 return employee.LastName () + ": " + employee.Badge (); 9 9 } 10 10 } 11 11 }
Person.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Person 4 4 { 5 5 public string Name { get ; set ; } = "" ; 6 6 7 7 public string LastName () => Name .Split (" " )[^1 ]; 8 8 9 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 10 } 11 11 }
View fixture on GitHub
# not-delegating-rejectedRefuses a member that does more than reach the inherited member
refusal
Target symbol M:Staff.Employee.LastName
Refuses not-delegating; every file is left unchanged
Employee.cs input
1 namespace Staff 2 { 3 public class Employee : Person 4 { 5 public new string LastName () => base .LastName ().ToUpperInvariant (); 6 } 7 }
Person.cs input
1 namespace Staff 2 { 3 public class Person 4 { 5 public string Name { get ; set ; } = "" ; 6 7 public string LastName () => Name .Split (" " )[^1 ]; 8 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 } 11 }
View fixture on GitHub
# nullableA forwarding member with nullable types is removed like any other
success
Target symbol P:Staff.Employee.Nickname
Employee.cs modified
1 1 #nullable enable 2 2 3 3 namespace Staff 4 4 { 5 5 public class Employee : Person 6 6 { 7 − public new string ? Nickname => base .Nickname ; 8 − 9 7 public string Display () => Nickname ?? "anonymous" ; 10 8 } 11 9 }
Person.cs unchanged
1 1 #nullable enable 2 2 3 3 namespace Staff 4 4 { 5 5 public class Person 6 6 { 7 7 public string ? Nickname { get ; set ; } 8 8 } 9 9 }
View fixture on GitHub
# overload-by-idRemoves the one overload the id names
success
Target symbol M:Shop.Report.Print(System.String)
Report.cs modified
1 1 namespace Shop 2 2 { 3 3 public class Report : Printer 4 4 { 5 − public new void Print (string text) => base .Print (text); 6 − 7 5 public new void Print (int number) => base .Print (number); 8 6 } 9 7 }
Printer.cs unchanged
1 1 namespace Shop 2 2 { 3 3 public class Printer 4 4 { 5 5 public void Print (string text) { } 6 6 7 7 public void Print (int number) { } 8 8 } 9 9 }
View fixture on GitHub
# overrideRemoves an override that only calls the member it overrides
success
Target symbol M:Shop.Report.Title
Report.cs modified
1 1 namespace Shop 2 2 { 3 3 public class Document 4 4 { 5 5 public virtual string Title () => "Document" ; 6 6 } 7 7 8 8 public class Report : Document 9 9 { 10 − public override string Title () => base .Title (); 11 − 12 10 public string Print () => "# " + Title (); 13 11 } 14 12 }
View fixture on GitHub
# property-with-setterRemoves a property whose getter and setter only reach the inherited property through base
success
Target symbol P:Staff.Employee.Name
Employee.cs modified
1 1 namespace Staff 2 2 { 3 3 public class Employee : Person 4 4 { 5 5 public decimal Salary { get ; set ; } 6 6 7 − public new string Name 8 − { 9 − get => base .Name ; 10 − set => base .Name = value ; 11 − } 12 − 13 7 public string Badge () => "Employee " + Name ; 14 8 } 15 9 }
Payroll.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Payroll 4 4 { 5 5 public string Slip (Employee employee) 6 6 { 7 7 employee.Name = "Ann Lee" ; 8 8 return employee.LastName () + ": " + employee.Badge (); 9 9 } 10 10 } 11 11 }
Person.cs unchanged
1 1 namespace Staff 2 2 { 3 3 public class Person 4 4 { 5 5 public string Name { get ; set ; } = "" ; 6 6 7 7 public string LastName () => Name .Split (" " )[^1 ]; 8 8 9 9 public string Greeting (string salutation) => salutation + " " + Name ; 10 10 } 11 11 }
View fixture on GitHub
# signature-differs-rejectedRefuses a member whose parameters differ from the inherited member's, here in a default value callers rely on
refusal
Target symbol M:Staff.Employee.Greeting(System.String)
Refuses signature-differs; every file is left unchanged
Employee.cs input
1 namespace Staff 2 { 3 public class Employee : Person 4 { 5 public new string Greeting (string salutation = "Hello" ) => base .Greeting (salutation); 6 7 public string Badge () => Greeting (); 8 } 9 }
Person.cs input
1 namespace Staff 2 { 3 public class Person 4 { 5 public string Name { get ; set ; } = "" ; 6 7 public string Greeting (string salutation = "Dear" ) => salutation + " " + Name ; 8 } 9 }
View fixture on GitHub