2009-02-06 10 views
6

Hier ist eine vereinfachte Version meiner Klasse:Kann eine Basisklasse feststellen, ob eine abgeleitete Klasse ein virtuelles Mitglied überschrieben hat?

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

Ich versuche, den Sperrcode von abgeleiteten Klassen zu verstecken. Aber ich möchte nur die Sperre erhalten, wenn die abgeleitete Klasse UpdateSharedData überschreibt; Wenn dies nicht der Fall ist, möchte ich nicht, dass die Methode alle anderen ausgeführten Instanzen blockiert und wartet, die freigegebene Daten aktualisieren, bevor sie die nicht freigegebenen Daten aktualisieren.

Die (scheinbar) naheliegende Sache, die Method zu tun hat, besteht darin, zu überprüfen, ob die Implementierung von UpdateSharedData durch die aktuelle Instanz die Implementierung der Basisklasse überschrieben hat. Ich bin mir ziemlich sicher, dass dies ohne Reflektion nicht möglich ist, und es ist wahrscheinlich nicht wünschenswert, das zu tun.

Ich habe gedacht, einige Abhilfen zu dieser, aber sie sind alle ziemlich umständlich:

  • eine geschützte Bool Eigenschaft hinzufügen, dass die Konstruktor-Sets der abgeleiteten Klasse, und prüfen Sie diese Eigenschaft, um zu sehen, ob eine Sperre erforderlich. Das macht eine ziemlich schreckliche Aufgabe, den Sperrcode vor den abgeleiteten Klassen zu verbergen.
  • Machen Sie die UpdateSharedData-Methode zu einer Delegateigenschaft, lassen Sie eine abgeleitete Klasse die Eigenschaft für eine private Methode in ihrem Konstruktor festlegen und erhalten Sie nur die Sperre, wenn der Delegat nicht null ist. Das ist besser, aber es ist immer noch irgendwie scheiße.

Antwort

4

Was passiert, wenn Sie eine abstrakte Aufgabe und eine IHasSharedData Schnittstelle definiert, dann in Methode überprüfen Sie, ob die abgeleitete Aufgabe IHasSharedData implementiert, bevor das tun sperren. Nur Klassen, die die Schnittstelle implementieren, müssen warten. Ich weiß, dass dies vermeidet, die eigentliche Frage zu beantworten, aber ich denke, es wäre eine sauberere Lösung als die Verwendung von Reflexion. Hoffentlich finden Sie einen besseren Namen für die Schnittstelle, die mehr dem entspricht, was die Klassen tatsächlich tun.

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

Ich habe gerade festgestellt, dass IHasSharedData dem LOLCats-Code sehr ähnlich sieht. :-) ICanHazSharedData? – tvanfosson

+0

Das war das erste, was mir auch aufgefallen ist! Es wird sehr schwer für mich sein, einem solchen Namen zu widerstehen. Das ist genau die Antwort, nach der ich gesucht habe. –

4

Sie können mit einem smidge der Reflexion dieses Kontroll tun:

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

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

Es wäre korrekter, m.DeclaringType direkt mit t zu vergleichen. Es ist sehr gut möglich, dass zwei verschiedene Typen denselben Namen haben. – JaredPar

+0

Das würde auch "true" melden, wenn es "neu" verwendet hätte, um die Methode erneut zu deklarieren (nicht zu überschreiben). –

+0

Gute Punkte. Festgestellt entsprechend. –

0

Eigentlich reden Sie zwei verschiedene Objekte:

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

Aber idealere Lösung wird die Strategie-Muster sein.

Verwandte Themen