2009-04-09 9 views
12

Mój problem polega na tym, że chciałbym przekazać obiekt do klasy pochodnej, ale musi to zostać wykonane przed konstruktorem klasy podstawowej, ponieważ klasa bazowa natychmiast wywoła wyprowadzenie metoda klasy Start(), która używa obiektu.Wykonanie konstruktora pochodnego przed konstruktorem bazowym w C#

Oto fragment z klasy bazowej (zmieniono nazwę z BarcodeScanner dla wygody).

public abstract class MyBase 
{  
    public MyBase() 
    { 
     if (Initialize()) 
      this.Start(); 
    } 

    public abstract bool Initialize(); 
    public abstract void Start(); 
} 

Oto klasa pochodna, którą tworzę.

class MyDerived : MyBase 
{ 
    private string sampleObject; 

    public MyDerived (string initObject) 
    { 
     sampleObject = initObject; 
    } 

    public override bool Initialize() 
    { 
     return GetDevice(); 
    } 
    public override void Start() 
    { 
     Console.WriteLine("Processing " + sampleObject.ToString()); 
    } 
} 

Wątpię, czy można wykonać C# wykonać konstruktor pochodny przed konstruktorem bazowym; tak naprawdę szukam rozwiązania do przekazania obiektu do klasy pochodnej, zanim obiekt zostanie użyty.

Zrobiłem to poprzez umieszczenie bloku Initialize/Start if wewnątrz konstruktora MyDerived. Istnieją jednak inne klasy wywodzące się z klasy bazowej; więc musiałem powtórzyć ten blok kodu Initialize/Start w każdej wyprowadzonej klasie. Chciałbym zobaczyć alternatywę dla modyfikacji klasy bazowej.

Odpowiedz

15

Co próbujesz zrobić jest niemożliwe w C#. Konstruktor w klasie bazowej musi być uruchomiony przed konstruktorem dowolnej klasy pochodnej, w przeciwnym razie wystąpiłby potencjał dla uszkodzonego obiektu. Obiekt podrzędny musi być w stanie przyjąć, że jego podstawa jest w pełni skonstruowana i dostępna.

+2

Możliwe jest utworzenie pustych nadpisań metod Initialize/Start i przeniesienie kodu do metod derivInitialize/derivedStart, a następnie w konstruktorze pochodnym wywołanie 'if (derivedInitialize()) derivedStart()'. Nie może być eleganckich rozwiązań, ale są jeszcze inne sposoby. – James

+0

Podczas gdy często dobrze jest, aby szkielet próbował zapobiec ekspozycji niekompletnie skonstruowanych obiektów, zdarzają się sytuacje, w których bardziej przydatna dla klasy byłaby możliwość dostępu do takich obiektów, z zastrzeżeniem, że musi być ona przygotowana z częściowo skonstruowanymi obiektami. Na przykład, jeśli klasa będzie enkapsulować obiekt 'IDisposable', którego czas życia będzie zgodny z jego własnym, byłoby wygodne użycie inicjalizatora pola do skonfigurowania; Niestety w C# nie ma czystej drogi, aby to zrobić bezpiecznie. Jeśli konstruktor klasy podstawowej wyrzuci, nic nie będzie w stanie uzyskać odniesienia do rzeczy wymagającej utylizacji. – supercat

17

IMHO Twój projekt jest zły. Nie należy rozpoczynać procesu od wewnątrz konstruktora. Twój konsumujący kod powinien jawnie wywołać metodę Start(), gdy jest to wymagane.

+0

+1 Wyjaśnienie wywołania "Start()". –

+0

+1. Ten facet jest poprawny, potrzebujesz .Start()/.Initialise(). Nie wywołuj metod wirtualnych w konstruktorach. – Quibblesome

+0

+1. C++ ogranicza powody połączeń wirtualnych z konstruktorów z jakiegoś powodu. –

0

Chciałbym przerobić twój projekt tak, że Initialize (i potencjalnie Start() - chociaż normalnie mam to być publiczną metodę, która jest wywoływana przez użytkownika) są wywoływane po zbudowaniu.

Jeśli tworzysz skaner kodów kreskowych, możesz to zrobić podczas pierwszego skanowania. Po prostu leniwy - zainicjuj swoich członków przy użyciu danych z klasy pochodnej.

To obejmie twój problem, bez rzeczywistej zmiany w użytkowaniu przez użytkownika.

+0

Skanowanie jest inicjowane asynchronicznie przez urządzenie, więc nie można zainicjować na podstawie uruchomienia skanowania. Z perspektywy czasu Microsoft powinien zmienić nazwę metody Start() na Gotowy(). Ponieważ metoda Start() tylko czyta skaner, ale w rzeczywistości nie rozpoczyna skanowania. – James

Powiązane problemy