Witam, co powiedziecie na taki kod?
class Employee { public Employee() { Init(); } public virtual void Init() { } } class Manager : Employee { public Manager() { } public override void Init() { } }
Czy jest to dobry design? Jeśli ktoś programował w C++, na pewno nie zgodzi się na wywoływanie jakiejkolwiek metody wirtualnej w konstruktorze. W CPP zostałaby wywołana metoda Employee:Init zamiast Manager:Init ponieważ w momencie tworzenia Employee, obiekt Manager jeszcze nie istnieje i dlatego nie może zostać wywołana Manager:Init. Myślę, że jest to bardzo intuicyjne. Obiekty są tworzone od tych podstawowych do tych najbardziej zagnieżdżonych. W momencie tworzenia rodzica, nie mamy dostępu do child’a. Wywoływanie metody wirtualnej jest zatem mylące, ponieważ wykonany zostanie kod, którego się nie spodziewaliśmy (Init Manager’a nigdy nie zostanie wykonany).
A jak to wygląda w przypadku C#? Konstruktory są również wywoływane od rodzica (Employee) do dzieci (Manager). Jednak w przeciwieństwie do CPP, metody wirtualne wywoływane są zawsze dla obiektów dziedziczących – czyli w tym przypadku zostanie wywołana prawidłowa metoda Manager:Init (a nie jak w CPP, Employee:Init). Również, w przeciwieństwie do CPP, wszelkie pola inicjalizowane w momencie deklaracji, będą dostępne z prawidłowymi wartościami w Init.
Czy to znaczy, że w CPP jest to bad practice, a w c# dopuszczalny? Nie, w obu językach opisana sytuacja jest zła i prezentuje brzydki kod. Wyobraźmy sobie taką modyfikację:
class Employee { public Employee() { Log(); } public virtual void Log() { } } class Manager : Employee { private string _data; public Manager(int id) { // zaladuj dane na podstawie ID _data = repository.Get(id); } public override void Log() { Console.Write(_data); } }
Co jeśli dodamy np. parametr do konstruktora aby zainicjalizować parę pól, które następnie metoda wirtualna będzie używać? Log zostanie wykonany przed inicjalizacją obiektu (wywołaniem właściwego konstruktora). Konstruktor jest w końcu odpowiedzialny za inicjalizację stanu obiektu i wykonywanie jakichkolwiek operacji na obiekcie, przed wywołaniem konstruktora jest niebezpieczne i może spowodować późniejszą niestabilnością obiektu.