2009-04-09 20 views
12

Il mio problema qui è che vorrei passare un oggetto a una classe derivata, ma deve essere fatto prima del costruttore della classe base, poiché la classe base chiamerà immediatamente il derivato il metodo Start() della classe che utilizza l'oggetto.Esegui un costruttore derivato prima del costruttore base in C#

Ecco un estratto dalla classe base, (rinominata da BarcodeScanner per comodità).

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

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

Ecco la classe derivata che sto creando.

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()); 
    } 
} 

Dubito che è possibile eseguire C# eseguire un costruttore derivato prima del costruttore base; quindi sto solo cercando una soluzione per passare un oggetto alla classe derivata prima che l'oggetto venga usato.

Ho ottenuto questo problema inserendo il blocco Initialize/Start if nel costruttore MyDerived. Tuttavia, ci sono altre classi derivanti dalla classe base; quindi ho finito per dover ripetere questo blocco di Initialize/Start code in ogni classe derivata. Mi piacerebbe vedere un'alternativa alla modifica della classe base.

risposta

15

Quello che stai cercando di fare è impossibile in C#. Un costruttore in una classe base deve essere eseguito prima del costruttore di qualsiasi classe derivata altrimenti potrebbe esserci potenziale stato dell'oggetto corrotto. Un oggetto figlio deve essere in grado di assumere che la sua base sia completamente costruita e disponibile.

+2

Bene, è possibile creare metodi di inizializzazione/avvio di sovrascrittura vuoti e spostare tali codici in metodi derivateInitialize/derivedStart, quindi nel costruttore derivato, chiamare 'if (derivedInitialize()) derivedStart()'. Non ci possono essere soluzioni eleganti, ma ci sono ancora altri modi. – James

+0

Mentre è spesso utile per un framework cercare di impedire l'esposizione di oggetti costruiti in modo incompleto, ci sono momenti in cui sarebbe più utile per una classe avere accesso a tali oggetti, con l'avvertenza che deve essere preparato per trattare con oggetti parzialmente costruiti. Ad esempio, se una classe incapsulerà un oggetto 'IDisposable' la cui durata corrisponderà al proprio, sarebbe conveniente usare un inizializzatore di campo per configurarlo; purtroppo in C# non c'è un modo pulito per farlo in modo sicuro. Se il costruttore della classe base getta, nulla sarà in grado di ottenere un riferimento alla cosa che ha bisogno di essere smaltita. – supercat

17

IMHO il tuo progetto è sbagliato. Non si dovrebbe avviare il processo dall'interno del costruttore. Il tuo codice utente dovrebbe chiamare esplicitamente il metodo Start() quando richiesto.

+0

+1 Per chiamate esplicite di "Start()". –

+0

+1. Questo tizio è corretto, hai bisogno. Start() /. Inizializzare(). Non chiamare metodi virtuali nei costruttori. – Quibblesome

+0

+1. C++ limita le chiamate virtuali dai costruttori per un motivo. –

0

Vorrei rielaborare il tuo disegno in modo che Initialize (e potenzialmente Start() - anche se normalmente avrei questo è un metodo pubblico che viene chiamato dall'utente) viene chiamato dopo la costruzione.

Se si sta creando un BarcodeScanner, è possibile eseguire questa operazione la prima volta che si esegue la scansione. Solo pigri: inizializza i membri utilizzando i dati della classe derivata.

Questo aggirerà il problema, senza alcun reale cambiamento nell'utilizzo da parte dell'utente.

+0

La scansione viene avviata in modo asincrono dal dispositivo, pertanto non è possibile inizializzare in base all'avvio della scansione. Con il senno di poi, Microsoft avrebbe dovuto rinominare il loro metodo Start() come metodo Ready(). Poiché il metodo Start() prepara solo lo scanner, ma in realtà non avvia la scansione. – James

Problemi correlati