Do not hide base class methods

Rule ND1205 Object Oriented Design

Method hiding: a base class defines non-virtual M() and a derived class also defines M() with the same signature, so base.M() and derived.M() behave differently — not polymorphism, where both invoke the same implementation on a derived instance. This leads to confusion; this rule warns about all such cases.

How to fix

Remove or rename the method, or change its parameter signature so it no longer hides the base method. Legitimate hiding (same name, different behavior) is very rare, see: https://learn.microsoft.com/en-us/archive/blogs/ericlippert/method-hiding-apologia

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Do not hide base class methods</Name>
// <Id>ND1205:DoNotHideBaseClassMethods</Id>
warnif count > 0

// Define a lookup table indexing methods by their name including parameters signature.
let lookup = Methods.Where(m => !m.IsConstructor && !m.IsStatic && !m.IsGeneratedByCompiler)
                    .ToLookup(m1 => m1.Name)

from t in Application.Types
where !t.IsStatic && t.IsClass &&
   // Discard classes deriving directly from System.Object
   t.DepthOfInheritance > 1 
where t.BaseClasses.Any()

// For each methods not overriding any methods (new slot), 
// let's check if it hides by name some methods defined in base classes.
from m in t.InstanceMethods
where m.IsNewSlot && !m.IsExplicitInterfaceImpl && !m.IsGeneratedByCompiler

// Notice how lookup is used to quickly retrieve methods with same name as m.
// This makes the query 10 times faster than iterating each base methods to check their name.
let baseMethodsHidden = lookup[m.Name].Where(m1 => m1 != m && t.DeriveFrom(m1.ParentType))

where baseMethodsHidden.Count() > 0
select new { 
   m, 
   baseMethodsHidden,
   baseClasses = baseMethodsHidden.ParentTypes(),
   Debt = 10.ToMinutes().ToDebt(),
   Severity = Severity.High
}

//<Expl>
// The *{0}* hides {2?a:several} method{2?:s} declared in *base {2}* instead of *overriding* {2?it:them}.
// This results in a lack of *polymorphic* behavior at runtime and leads to confusion.
//</Expl>

//<Desc>
// Method hiding: a base class defines non-virtual *M()* and a derived class
// also defines *M()* with the same signature, so *base.M()* and
// *derived.M()* behave differently — not *polymorphism*, where both invoke
// the same implementation on a *derived* instance. This leads to confusion;
// this rule warns about all such cases.
//</Desc>

//<HowToFix>
// Remove or rename the method, or change its parameter signature so it no
// longer hides the base method. Legitimate hiding (same name, different
// behavior) is very rare, see:
// https://learn.microsoft.com/en-us/archive/blogs/ericlippert/method-hiding-apologia
//</HowToFix>

Object Oriented Design: all 14 items