6 Stimmen

Kann eine Basisklasse feststellen, ob eine abgeleitete Klasse ein virtuelles Element überschrieben hat?

Hier ist eine vereinfachte Version meiner Klasse:

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 vor abgeleiteten Klassen zu verbergen. Ich möchte jedoch nur das Schloss erhalten, wenn die abgeleitete Klasse UpdateSharedData außer Kraft setzt; Wenn es das nicht tut, möchte ich nicht, dass die Methode blockiert und auf alle anderen laufenden Instanzen wartet, die gemeinsame Daten aktualisieren, bevor sie die nicht gemeinsamen Daten aktualisiert.

Also das (scheinbar) offensichtliche, das Methode tun sollte, ist zu überprüfen und zu sehen, ob die Implementierung von UpdateSharedData der aktuellen Instanz die Implementierung der Basisklasse überschrieben hat. Ich bin mir ziemlich sicher, dass dies ohne Verwendung von Reflexion nicht möglich ist, und es ist wahrscheinlich nicht wünschenswert, das zu tun.

Ich habe mir einige Workarounds dazu überlegt, aber sie sind alle ziemlich umständlich:

  • Fügen Sie eine geschützte boolsche Eigenschaft hinzu, die der Konstruktor der abgeleiteten Klasse festlegt, und überprüfen Sie diese Eigenschaft, um zu sehen, ob ein Schloss erforderlich ist. Das Verbergen des Sperrcodes vor den abgeleiteten Klassen ist keine besonders gute Idee.
  • Machen Sie die UpdateSharedData-Methode zu einer Delegat-Eigenschaft, lassen Sie jede abgeleitete Klasse die Eigenschaft auf eine private Methode in ihrem Konstruktor setzen, und erhalten Sie das Sperreigenschaft, wenn der Delegat nicht null ist. Das ist besser, aber es ist immer noch ziemlich ärgerlich.

5voto

Chris Hynes Punkte 9589

Sie können diesen Check mit nur ein bisschen Reflexion durchführen:

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

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

0 Stimmen

Es wäre korrekter, m.DeclaringType direkt mit t zu vergleichen. Es ist durchaus möglich, dass zwei unterschiedliche Typen denselben Namen haben.

0 Stimmen

Das würde auch dann true zurückgeben, wenn es "new" verwendet, um die Methode neu zu deklarieren (nicht zu überschreiben).

0 Stimmen

Es funktioniert immer noch nicht zu 100%. Eine Methode kann sowohl virtuell als auch "neu" sein.

5voto

tvanfosson Punkte 506878

Was ist, wenn Sie eine abstrakte Aufgabe und eine IHasSharedData-Schnittstelle definieren und dann in der Methode überprüfen, ob die abgeleitete Aufgabe IHasSharedData implementiert, bevor Sie das Schloss verwenden. Nur Klassen, die das Interface implementieren, müssen warten. Mir ist klar, dass dies die eigentliche Frage nicht beantwortet, aber ich denke, es wäre eine sauberere Lösung als die Verwendung von Reflektion. Hoffentlich finden Sie einen besseren Namen für die Schnittstelle, der näher an das herankommt, 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 Stimmen

Ich habe gerade festgestellt, dass IHasSharedData sehr ähnlich aussieht wie der LOLCats-Code. :-) ICanHazSharedData?

0 Stimmen

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

0voto

LicenseQ Punkte 1681

Eigentlich sprechen Sie über zwei unterschiedliche 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 die idealere Lösung wäre das Strategie-Muster.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X