Constructor should not call a virtual method

Rule ND1209 Object Oriented Design

Matches constructors of a non-sealed class calling virtual methods.

Constructors run base-first, but virtual calls always execute on the most derived type. A virtual call in a constructor may thus run on a class whose constructor hasn't executed yet — an unsuitable state — making the class error-prone to derive from.

How to fix

Re-design object initialization or declare the class sealed if possible. A constructor should initialize its own class's state; extra setup belongs in derived constructors or methods called after construction.

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>Constructor should not call a virtual method</Name>
// <Id>ND1209:ConstructorShouldNotCallAVirtualMethod</Id>
warnif count > 0

from t in JustMyCode.Types where 
   t.IsClass &&
  !t.IsGeneratedByCompiler &&
  !t.IsSealed

from ctor in t.Constructors 
let virtualMethodsCalled = 
   from mCalled in ctor.MethodsCalled
   where mCalled.IsVirtual && !mCalled.IsFinal && 
         // Only take care of just-my-code virtual methods called
         JustMyCode.Contains(mCalled) &&
         (  mCalled.ParentType == t ||
           (t.DeriveFrom(mCalled.ParentType) && 
            // Don't accept Object methods since they can be called
            // from another reference than the 'this' reference.
            mCalled.ParentType.FullName != "System.Object")
         )
   select mCalled
where virtualMethodsCalled.Count() > 0

select new { 
   ctor , 
   virtualMethodsCalled, 
   // If there is no derived type, it might be 
   // an opportunity to mark t as sealed.
   t.DerivedTypes,
   Debt = ((virtualMethodsCalled.Count())*4).ToMinutes().ToDebt(),
   Severity = Severity.High
}

//<Expl>
// The *{0}* should not call the *virtual {1}* because doing so 
// can potentially corrupt the object initialization process.
//</Expl>

//<Desc>
// Matches constructors of a non-sealed class calling virtual methods.
//
// Constructors run base-first, but virtual calls always execute on the most
// derived type. A virtual call in a constructor may thus run on a class
// whose constructor hasn't executed yet — an unsuitable state — making the
// class *error-prone* to derive from.
//</Desc>

//<HowToFix>
// Re-design object initialization or declare the class *sealed* if possible.
// A constructor should initialize its own class's state; extra setup belongs
// in derived constructors or methods called after construction.
//</HowToFix>

Object Oriented Design: all 14 items