2011-11-03 17 views
15

Desidero poter chiamare automaticamente un metodo particolare sulla costruzione di un oggetto derivato, tuttavia non riesco a pensare a come farlo. Il seguente codice illustra. Un'altra risposta raccomandata OnLoad, ma lo sto facendo per Unity su Mac e OnLoad non sembra essere supportato dalla mia piattaforma. Eventuali suggerimenti?C'è un modo per chiamare automaticamente un metodo particolare immediatamente dopo l'esecuzione di tutti i costruttori?

public class Parent { 

    public Parent() 
    { 
     // A. Stuff to do before child constructor code runs 
     DoThisAutomaticallyAfterConstruction(); 
    } 

    public void DoThisAutomaticallyAfterConstruction() 
    { 
     // C. In this example, this will run after A, before B. I want it to run ABC 
    } 
} 

public class Child : Parent { 

    public Child() : base() 
    { 
     // B. Stuff to do here after parent constructor code runs 
    } 
} 
+2

questo è l'odore del codice per me. la classe base non dovrebbe riguardare le classi derivate. inoltre, i costruttori dovrebbero impostare lo stato inizializzato dell'oggetto. – Jason

+1

+1 @ jason - sono d'accordo. – JonH

risposta

13

Sfortunatamente non esiste un modo integrato per fare ciò che si desidera (è una caratteristica abbastanza spesso richiesta).

Una soluzione alternativa consiste nell'implementare un modello di fabbrica, in cui non si creano oggetti chiamando direttamente il costruttore, ma invece si implementa un metodo statico per crearli. Per esempio:

public class MyClass 
{ 
    public MyClass() 
    { 
    // Don't call virtual methods here! 
    } 

    public virtual void Initialize() 
    { 
    // Do stuff -- but may be overridden by derived classes! 
    } 
} 

quindi aggiungere:

public static MyClass Create() 
{ 
    var result = new MyClass(); 

    // Safe to call a virtual method here 
    result.Initialize(); 

    // Now you can do any other post-constructor stuff 

    return result; 
} 

e invece di fare

var test = new MyClass(); 

si può fare

var test = MyClass.Create(); 
+0

Ah, geniale! Ho sentito parlare di fabbriche, ma questo mi aiuta a capirle molto di più. –

+0

@RobinKing Vedere la mia risposta modificata per un paio di indicazioni per ulteriori dettagli sul modello di fabbrica. Il metodo statico di fabbrica è un approccio semplice, ma alcuni preferiscono utilizzare oggetti di fabbrica (e alcune situazioni richiedono il loro uso). – phoog

+1

Questo è un post molto vecchio, ma potrebbe essere utile cambiare il costruttore in 'private', questo costringerà i clienti a usare la fabbrica. –

2

Basato sul tuo esempio si sta realizzando ACB, si vuole realizzare ABC.

Per eseguire il codice dopo il costruttore figlio, è necessario effettuare la chiamata dopo B (il costruttore figlio) non è possibile chiamare il codice in A (il costruttore padre), quindi non si eseguirà ABC.

Spostare DoThisAutomaticallyAfterConstruction() alla fine del costruttore classe figlio?

Davvero una strana domanda però.

+0

Purtroppo, questo non fa necessariamente il trucco in una classe non sealed, dal momento che le classi ancora più derivate potrebbero eseguire un'ulteriore inizializzazione che avverrà dopo che il costruttore figlio ha terminato l'esecuzione, quindi non è ancora sicuro chiamare membri virtuali nel costruttore. –

+1

@JeremyTodd - Avremmo bisogno di più informazioni dall'OP se fosse il caso. Rispondo alle informazioni di questo post. – JonH

+0

In codice normale è strano, ma quando si ha a che fare con il codice UI questo è spesso necessario. Ma come diceva l'autore, normalmente useremmo l'evento OnLoad. –

4

Questo sembra un buon candidato per una fabbrica. Rendi privati ​​o protetti tutti i costruttori, richiedendo agli utenti del tuo codice di chiamare il metodo factory quando vogliono un'istanza del tuo oggetto. Nel metodo factory, si utilizza l'operatore new per creare l'oggetto, quindi chiamare DoThisAutomaticallyAfterConstruction() prima di restituire l'oggetto.

EDIT

Una fabbrica può essere un metodo statico, o si può avere un oggetto di fabbrica. Vedere, ad esempio, Wikipedia sul modello di fabbrica astratto a http://en.wikipedia.org/wiki/Abstract_factory_pattern e la documentazione per DbProviderFactory di ADO.NET a http://msdn.microsoft.com/en-us/library/wda6c36e.aspx per un'implementazione reale.

+0

Ha perfettamente senso. Grazie! –

0

Mentre (accettati) opere risposta di @Jeremy Todd ed è una soluzione ampiamente accettata per il problema , ha uno svantaggio: non molto compatibile con IoC e serializzazione, dal momento che la tua classe non può essere costruita correttamente usando new. Permettetemi di introdurre una soluzione generale usando alcune caratteristiche di C#. Si noti che questa soluzione non richiede l'utilizzo di un pattern factory o richiama nulla dopo la costruzione dell'oggetto e funziona su qualsiasi classe con l'implementazione di un'interfaccia con un singolo metodo. In primo luogo si dichiara un'interfaccia che le nostre classi dovranno realizzare:

public interface IInitialize { 
    void OnInitialize(); 
} 

Poi si aggiunge una classe estensione statica per questa interfaccia, e aggiungere il metodo di inizializzazione:

public static class InitializeExtensions 
{ 
    public static void Initialize<T>(this T obj) where T: IInitialize 
    { 
     if (obj.GetType() == typeof(T))  
      obj.OnInitialize(); 
    } 
} 

Ora, se abbiamo bisogno di una classe e tutti i suoi discendenti a chiamare un inizializzatore destra dopo che l'oggetto è completamente costruito, tutto quello che dobbiamo fare è implementare IInitialize e di aggiunta di una linea nel costruttore:

public class Parent : IInitialize 
{ 
    public virtual void OnInitialize() 
    { 
     Console.WriteLine("Parent"); 
    } 

    public Parent() 
    { 
     this.Initialize(); 
    } 
} 

public class Child : Parent 
{ 
    public Child() 
    { 
     this.Initialize(); 
    } 

    public override void OnInitialize() 
    { 
     Console.WriteLine("Child"); 
    } 
} 

public class GrandChild : Child 
{ 
    public GrandChild() 
    { 
     this.Initialize(); 
    } 

    public override void OnInitialize() 
    { 
     Console.WriteLine("GrandChild"); 
    } 
} 

Il trucco è che quando una classe derivata chiama il metodo di estensione Initialize, questo sopprimerà le chiamate non effettuate dalla classe effettiva.

Problemi correlati