Domanda

Il mio problema è che mi piacerebbe passare un oggetto di una classe derivata, ma deve essere fatto prima che il costruttore della classe base, dal momento che la classe base verrà immediatamente chiamare il metodo Start() della classe derivata che utilizza l'oggetto.

Ecco un estratto dalla classe base, (rinominato 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 si possa fare C # eseguire un costruttore derivato prima che il costruttore di base; quindi sono davvero solo in cerca di una soluzione per passare un oggetto per la classe derivata prima di utilizzare l'oggetto.

Ho ottenuto intorno a questo mettendo l'inizializzazione / Start blocco if all'interno del costruttore MyDerived. Tuttavia, ci sono altre classi derivate dalla classe base; così ho finito per dover ripetere questo blocco di codice di inizializzazione / Start in ogni classe derivata. Mi piacerebbe vedere un'alternativa per modificare la classe di base.

È stato utile?

Soluzione

Che cosa si sta cercando di fare è impossibile in C #. Un costruttore in una classe base deve essere eseguito prima del costruttore di qualsiasi classe derivata altrimenti ci sarebbe stato potenziale per oggetto corrotto. Un oggetto bambino deve essere in grado di assumere che la sua base è completamente costruito e disponibile.

Altri suggerimenti

IMHO il vostro disegno è sbagliato. Non si dovrebbe avviare il processo dall'interno del costruttore. Il codice di consumo deve chiamare esplicitamente il metodo Start () quando richiesto.

Vorrei rielaborare il vostro disegno in modo che Initialize (e potenzialmente Start () - anche se mi piacerebbe normalmente hanno questo sia un metodo pubblico che si chiama dall'utente) sono chiamati dopo la costruzione

.

Se stai facendo un BarcodeScanner, si potrebbe fare questo la prima volta che vai per la scansione. Solo pigro-inizializzare i vostri membri utilizzando i dati dalla classe derivata.

In questo modo aggirare il problema, senza alcun reale cambiamento utilizzo da parte dell'utente.

Ci scusiamo per l'aggiunta di un vecchio thread, ma forse qualcuno è interessato in un'altra risposta. Ho trovato un (IMO) modo pulito per gestire la logica di fare qualcosa di più (e dopo) solo i campi assegnazione a costruttori della classe in cui l'ereditarietà è coinvolto qui . Se si vogliono solo avere se per questa gerarchia specifica e non utilizzare la soluzione generica con un'interfaccia e un metodo di estensione, è possibile utilizzare lo stesso concetto in un albero classe come questa:

public abstract class MyBase
{    
    protected MyBase()
    {
        if (Initialize(this)) // just to illustrate; this will never pass here as this class is abstract
            this.Start();
    }

    protected bool IsInitialized { get; private set; } = false;

    protected static bool Initialize<T>(T instance) where T: MyBase
    {
        if (instance?.GetType() == typeof(T)) // check if this is called from the constructor of instance run time type
            return instance.IsInitialized || ( instance.IsInitialized = instance.Initialize() );
        return false;
    }

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

e derivati:

class MyDerived : MyBase
{
    private string sampleObject;
    protected bool started = false;

    public MyDerived (string initObject)
    {
        sampleObject = initObject;
        if (Initialize(this)) // if this is the most derived constructor, this will run Initialize() and return whether it was successful
            this.Start();
    }

    protected override bool Initialize() 
    { 
       return GetDevice();
    }

    public override void Start() 
    { 
        // if Start() would be protected, we don't need the IsInitialized property and we can move this check to the constructor on the returned value of the Initialize<T>() call.
        if (!IsInitialized) throw new InvalidOperationException("Initialization failed.");
        // if you want to have this method exposed public, we need to check if this instance is successfully initialized from the constructor and not in started state already.
        if (started) return;

        Console.WriteLine("Processing " + sampleObject.ToString()); 
        started = true;
        if (!Run(sampleObject)) started = false;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top