2015-06-06 9 views
5

Ich habe eine untergeordnete Klasse Bicycle, die von Agent erbt. Der Agent hat eine Eigenschaft, die von dem Fahrrad abhängt, um es zu definieren. Das physikalische Modell für den Agenten muss nämlich mit den Beschränkungen für Geschwindigkeit und Beschleunigung initialisiert werden, die auf einer Fahrradbasis definiert sind und für einen anderen Agententyp unterschiedlich sein würden.Erzwingen einer untergeordneten Klasse zum Initialisieren einer übergeordneten Eigenschaft nach der Berechnung

Das Problem, das ich habe, ist, dass ich nicht die Parameter, die ich berechnen muss (die Geschwindigkeit/Beschleunigung erfordern Berechnungen, um sie aus einer theoretischen Verteilung zu ziehen) dafür im base() Konstruktor übergeben, da natürlich die Kindklasse noch nicht wurde instanziiert.

Die Berechnungen werden einmal pro Fahrradinstanz durchgeführt, werden aber mehrfach verwendet, so dass eine einfache statische Methode nicht ausreicht. Ich kann einfach eine protected Methode im Elternteil aufrufen, nachdem sie berechnet wurden, aber AFAIK gibt es keine Möglichkeit, dies in dem Kind zu erzwingen, oder genauer gesagt in zukünftigen Kindern, die ich vielleicht nicht schreibe.

So zum Beispiel könnte ich:

public abstract class Agent 
{ 
    protected IPhysics PluginPhysics { get; set; } 

    protected Agent(...) 
    { 
    } 
} 

public class Bicycle : Agent 
{ 
    private double maxA; 

    public Bicycle(Object anotherParameter) : base(...) 
    { 
     maxA = ComputationOfMaxA(); 
     this.PluginPhysics = new Physics(anotherParameter, maxA); 
    } 

    private static double ComputationOfMaxA() 
    { 
     ... 
    } 
    ... 
} 

oder ich kann:

public abstract class Agent 
{ 
    protected IPhysics PluginPhysics { get; private set; } 

    protected Agent(...) 
    { 
    } 

    protected void SetupPhysics(Physics physics) 
    { 
     this.PluginPhysics = physics; 
    } 
} 

public class Bicycle : Agent 
{ 
    private double maxA; 

    public Bicycle(Object anotherParameter) : base(...) 
    { 
     maxA = ComputationOfMaxA(); 
     SetupPhysics(new Physics(anotherParameter,maxA)); 
    } 

    private static double ComputationOfMaxA() 
    { 
     ... 
    } 

    ... 
} 

Ich würde lieber auch nicht von denen tun, da es keine Kompilierung-Art und Weise ist, dass das Kind, um sicherzustellen, Initialisiert PluginPhysics, dass ich denken kann, und ich würde lieber PluginPhysics nicht in der Lage sein, geändert werden, sobald es initialisiert wurde. Ich hätte auch lieber nicht die Teile der Parameter, die in Physics außerhalb der Bicycle Klasse gehen müssen. Ich schätze, dass all diese Dinge möglicherweise nicht gleichzeitig möglich sind.

So kurz von stark formulierten Dokumentation oder eine Reihe von Laufzeit Null-Tests in der Elternklasse, bevor irgendwelche der relevanten Klassenobjekte aufgerufen werden, gibt es eine offensichtliche C# -artige Art, die ich vermisse Forcen ein Kind ein Elternklasse Feld vor der Verwendung zu initialisieren, wenn Sie nicht im Konstruktor tun können?

+2

Ich habe Ihren Code ein wenig verbessert, um es deutlicher zu machen, dass Sie sich darauf verlassen, dass ComputationOfMaxA vollständig ist, bevor Sie das Physik-Objekt erstellen. –

Antwort

3

d4Rk's answer war ganz in der Nähe, man sollte jedoch versuchen, virtuelle Methoden wie bad things can happen von einem Konstruktor nicht aufrufen. Wenn Sie jedoch eine Kombination aus Lazy Loading Tricks und ISupportInitialize verwenden, können Sie die Erstellung des Plugins verzögern, bis der Konstruktor fertig ist.

public abstract class Agent : ISupportInitialize 
{ 
    private bool _initialized = false; 

    private IPhysics _pluginPhysics; 
    protected IPhysics PluginPhysics 
    { 
     get 
     { 
      if(!_initialized) 
       EndInit(); 
      return _pluginPhysics; 
     } 
    } 

    protected Agent(...) 
    { 
    } 

    protected abstract IPhysics CreatePhysics(); 

    ISupportInitialize.BeginInit() 
    { 
     //We make this a explicit implementation because it will not 
     //do anything so we don't need to expose it. 
    } 

    public void EndInit() 
    { 
     if(_initialized) 
      return; 

     _initialized = true; 
     _pluginPhysics = CreatePhysics(); 
    } 
} 

public class Bicycle : Agent 
{ 
    private double maxA; 
    Object _anotherParameter; 

    public Bicycle(Object anotherParameter) 
    { 
     _anotherParameter = anotherParameter; 
    } 
    protected override IPhysics CreatePhysics() 
    { 
     ComputationOfMaxA(); 
     return new Physics(anotherParameter, maxA); 
    } 
} 

Der Benutzer Ihrer Klasse muß EndInit() anrufen, nachdem sie ein Objekt zurück zu bewirken, dass das IPhysics Objekt zu erstellenden bekommen, aber wenn sie die Initialize-Funktion, um den Getter auf dem Physik-Objekt zu nennen vergessen lösen die initialisiere den Anruf selbst, wenn er das erste Mal benutzt wird.

Sie könnten alles tun, was ich ohne die ISupportInitialize Schnittstelle gezeigt habe und nur eine öffentliche Initalize() Methode auf der Basisklasse, aber ich möchte Framework-Schnittstellen offen legen, wenn sie passen.

+0

Dies ist eleganter als die Art von Null-Überprüfung, an die ich dachte, da es eine richtige Schnittstelle verwendet, die explizit für die Initialisierung von "co-abhängigen Eigenschaften" ist, was im Wesentlichen das ist, was ich hier habe. Es fühlt sich sicher am meisten "C# ish" an. Vielen Dank. – Smalltown2k

2

Was ist mit Erzwingen der Unterklasse, um eine CreatePhysics Methode zu implementieren, und rufen Sie dies in der Basis ctor?

So:

public abstract class Agent 
{ 
    protected IPhysics PluginPhysics { get; private set; } 

    protected Agent(...) 
    { 
     var physics = CreatePhysics(); 
     SetupPhysics(physics); 
    } 

    void SetupPhysics(IPhysics physics) 
    { 
     this.PluginPhysics = physics; 
    } 

    protected abstract IPhysics CreatePhysics(); 
} 

public class Bicycle : Agent 
{ 
    private double maxA; 

    protected override IPhysics CreatePhysics() 
    { 
     ComputationOfMaxA(); 
     return new Physics(maxA); 
    } 
} 
+0

Das ist großartig, solange das Physik-Plugin nicht auch Dinge benötigt, die über den Bicycle-Konstruktor kommen ... was leider mein Fall ist. Ich könnte die gestellte Frage aktualisieren. – Smalltown2k

+0

Im Allgemeinen ist es verpönt, im Konstruktor eine abstrakte/virtuelle Klasse aufzurufen. Dies liegt daran, dass untergeordnete Konstruktoren noch nicht gestartet wurden und Variablen auf Klassenebene noch nicht instanziiert wurden. Wenn Sie mehr als eine Vererbungsebene haben, kann dies sehr schwierig werden. –

+0

Wenn Sie jedoch diese Methode mit ['ISupportInitialize'] kombinieren (https://msdn.microsoft.com/en-us/library/system.componentmodel.isupportinitialize%28v=vs.110%29.aspx), dann haben Sie 'EndInit()' Aufruf 'CreatePhysics()' alles wird gut. –

2

Wie über den Konstruktor für Agent machen, um das IPhysics Objekt und es protected Dann in Ihrer Bicycle Klasse zu machen, sind Sie gezwungen, den Konstruktor auf der Basis zu nennen, die Ihre Klasse einrichtet Immobilie:

public class Agent 
{ 
    protected IPhysics PluginPhysics { get; private set; } 

    protected Agent(IPhysics physicsPlugin) 
    { 
     PluginPhysics = physicsPlugin; 
    } 
} 

public class Bicycle : Agent 
{ 
    public Bicycle(IPhysics physicsPlugin) 
     : base(physicsPlugin) 
    { 
     Console.WriteLine("Bicycle ctor"); 
    } 
} 
+0

Und wo führst du die Berechnungen für 'physicsPlugin' durch? – d4Rk

+0

Ich könnte dazu kommen. Was ich versuche zu vermeiden, sind bestimmte Dinge, die in diesen Physik-Konstruktor gehen müssen, wenn man außerhalb der Bicycle-Klasse ist. Obwohl ich weiß, dass das nicht in der Frage war, die ich jetzt aktualisiert habe. – Smalltown2k

+0

Wenn Sie diese Teile nicht außerhalb der Bicycle-Klasse haben wollen, warum sollten Sie dann nicht alles intern zum 'Agent' machen? Ich denke, ich bin etwas verwirrt darüber, was genau Sie hier erreichen wollen. – DavidG

Verwandte Themen