2009-02-06 5 views
6

Ecco una versione semplificata della mia classe:Una classe base può determinare se una classe derivata ha sovrascritto un membro virtuale?

public abstract class Task 
{ 
    private static object LockObject = new object(); 

    protected virtual void UpdateSharedData() { } 
    protected virtual void UpdateNonSharedData() { } 

    public void Method() 
    { 
     lock(LockObject) 
     { 
      UpdateSharedData(); 
     } 
     UpdateNonSharedData(); 
    } 
} 

sto cercando di nascondere il codice di blocco da classi derivate. Ma voglio solo ottenere il blocco se la classe derivata sovrascrive UpdateSharedData; in caso contrario, non voglio che il metodo blocchi e attenda su tutte le altre istanze in esecuzione che aggiornano i dati condivisi prima di aggiornare i dati non condivisi.

Quindi la cosa (apparentemente) ovvia per il Metodo da fare è controllare e vedere se l'implementazione dell'istanza corrente di UpdateSharedData ha superato l'implementazione della classe base. Sono abbastanza sicuro che questo non è possibile senza l'uso del riflesso, e probabilmente non è desiderabile farlo.

ho pensato di alcune soluzioni a questo, ma sono tutti piuttosto imbarazzante:

  • aggiungere una proprietà bool protetta che i set di costruzione della classe derivata, e verificare che la proprietà per vedere se un blocco è necessario. Questo sta facendo un terribile compito di nascondere il codice di blocco dalle classi derivate.
  • Rendi il metodo UpdateSharedData una proprietà delegata, imposta una classe derivata la proprietà a un metodo privato nel suo costruttore e ottieni il blocco solo se il delegato non è nullo. È meglio, ma è ancora un po 'schifoso.

risposta

4

Che cosa succede se si è definito una task astratto e un'interfaccia IHasSharedData, poi nel metodo di controllare se il compito derivata implementa IHasSharedData prima di fare il serratura. Solo le classi che implementano l'interfaccia devono attendere. Mi rendo conto che questo evita di rispondere alla domanda reale, ma penso che sarebbe una soluzione più pulita rispetto all'utilizzo della riflessione. Si spera che troviate un nome migliore per l'interfaccia che corrisponda maggiormente a quello che effettivamente fanno le classi.

public interface IHasSharedData 
{ 
    void UpdateSharedData(); 
} 

public abstract class Task 
{ 
    private static object LockObject = new object(); 

    protected virtual void UpdateNonSharedData() { } 

    public void Method() 
    { 
     if (this is IHasSharedData) 
     { 
      lock(LockObject) 
      { 
       UpdateSharedData(); 
      } 
     } 
     UpdateNonSharedData(); 
    } 
} 

public class SharedDataTask : Task, IHasSharedData 
{ 
    public void UpdateSharedData() 
    { 
     ... 
    } 
} 
+0

Ho appena realizzato che IHasSharedData assomiglia molto al codice LOLCats. :-) ICanHazSharedData? – tvanfosson

+0

Questa è stata la prima cosa che ho notato anche io! Sarà molto difficile per me resistere nel dargli un nome del genere. Questa è esattamente la risposta che stavo cercando comunque. –

4

Si può fare questo controllo con uno smidge di riflessione:

bool IsUpdateSharedDataOverridden() 
{ 
    Type t = this.GetType(); 
    MethodInfo m = subType.GetMethod("UpdateSharedData"); 

    return m.DeclaringType == t && m.GetBaseDefinition().DeclaringType == typeof(Task); 
} 
+0

Sarebbe più corretto confrontare m.DeclaringType direttamente con t. È possibile che due tipi diversi abbiano lo stesso nome. – JaredPar

+0

Questo dovrebbe anche riportare vero se ha usato "nuovo" per ri-dichiarare (non sovrascrivere) il metodo. –

+0

Buoni punti. Risolto di conseguenza. –

0

In realtà, si sta parlando di due diversi oggetti:

public abstract class Task {  
    protected virtual void UpdateNonSharedData() { } 

    public virtual void Method()  
    { 
     UpdateNonSharedData();  
    } 
} 

public abstract class TaskWithSharedData : Task {  
    private static object LockObject = new object();  

    protected virtual void UpdateSharedData() { } 

    public overrides void Method()  
    {  
     lock(LockObject) 
     {   
      UpdateSharedData();  
     } 
     base.Method(); 
    } 
} 

Ma, la soluzione più ideale sarà il modello di strategia.