Metodi virtuali nel costruttore

Tra le guidelines da seguire circa la scrittura del costruttore di una classe questa assume una certa importanza:

Evitare di richiamare all’interno di un costruttore un metodo virtuale.

Questa guidelines è dovuta al fatto che in presenza di un metodo che ridefinisce un metodo virtuale (ne fa l’override insomma), viene richiamato sempre il metodo esposto dalla classe derivata, ovvero il metodo più specifico della implementazione, a prescindere se il costruttore della classe che espone il metodo derivato sia stato richiamato.  L’esempio sotto, tratto dalla documentazione MSDN, chiarisce bene questo concetto.

La creazione della classe DerivedFromBad, cioè il richiamo del suo costruttore, provoca la chiamata immediata  al costruttore della sua classe di base, ovvero BadBaseClass, il quale prima inizializza una variabile (state) e poi richiama il metodo virtuale SetState. Peccato che poiche l’oggetto che si sta cercando di costruire (DerivedFromBad) ridefinisce il metodo SetState, all’interno del costruttore della classe BaseClass il metodo effettivamente chiamato è l’implementazione fornita dalla classe DerivedFromBad, e non quella della classe base BadBaseClass. L’esecuzione  del metodo ridefinito SetState avviene a prescindere se il costruttore della classe derivata sia o no stato richiamato. In questo caso, poiche non  viene richiamato, l’inizializzazione della variabile state non  avviene ed il suo contenuto resta fermo a quello impostato dalla classe base, ovvero “BadBaseClass“.

 public class tester
 {
     public static void Main()
     {
         DerivedFromBad b = new DerivedFromBad();
     }
 }
      
 public class BadBaseClass
 {
      protected string state;
      public BadBaseClass()
      {
          state = "BadBaseClass";
          SetState();
      }
      public virtual void SetState()
      {
 
      }
 }
   
 public class DerivedFromBad : BadBaseClass
 {
      public DerivedFromBad()
      {
        state = "DerivedFromBad ";
      }
      public override void SetState()
      {
        Console.WriteLine(state);
      }
 }