C# Vererbung Designfrage



  • Hallo,

    ich hab ein Problem mit dem Vererben und Überschreiben von Funktionen.
    Ich habe eine Klasse Control und davon z.B. eine Klasse Label abgeleitet:

    class Control
    {
    	protected Point location;
    	public virtual Point Location
    	{
    		get
    		{
    			return location;
    		}
    		set
    		{
    			location = value;
    		}
    	}
    
    	public Control()
    	{
    		Location = new Point(3, 3); //setze Defaultposition
    	}
    }
    
    class Label : Control
    {
    	private MyRectangle bounds;
    
    	public override Point Location
    	{
    		get
    		{
    			return base.Location;
    		}
    		set
    		{
    			base.Location = value;
    			bounds.X = value.X; //hier kommts zum Crash weil bounds noch gar nicht existiert
    			bounds.Y = value.Y;
    		}
    	}
    
    	public Label()
    	{
    		bounds = new MyRectangle(location.X, location.Y, 10, 10);
    	}
    }
    
    class MyRectangle
    {
    	public int X,
    			   Y,
    			   W,
    			   H;
    
    	public MyRectangle(int x, int y, int w, int h)
    	{
    		X = x;
    		Y = y;
    		W = w;
    		H = h;
    	}
    }
    

    Aktionen wenn ich ein neues Label erstelle:
    1. Control Konstruktor wird aufgerufen
    2. Location soll der Defaulwert zugewiesen werden
    3. anstatt Control.Location wird Label.Location aufgerufen
    4. es kommt zum Crash, da bounds erst im Construktor von Label erzeugt wird, dieser wird aber erst nach dem Construktor von Control aufgerufen.

    Von C++ bin ich es gewohnt, dass der Construktor nur Funktionen aus seinem eigenen Scope aufruft und nicht die, die irgendwann einmal überschrieben werden von einer Unterklasse eben aus dem Grund, weil die Unterklasse zu diesem Zeitpunkt noch gar nicht existieren kann.
    Warum ist das Verhalten in C# anders?

    Beispiele zum Vergleichen:
    C# http://ideone.com/lW4W5
    C++ http://ideone.com/evScI

    greetz KN4CK3R



  • KN4CK3R schrieb:

    Warum ist das Verhalten in C# anders?

    Warum nicht? Ich finde das Verhalten von C# (und Java) auf jeden Fall logischer und nützlicher. Das Problem ist in C# und C++ dasselbe nur sagt C++ hier einfach "Geht nicht". Aber wenn du das Problem kennst, kannst dir auch überlegen, wie du das am geschicktesten lösen kannst.



  • für mich als C++ Programmierer ist das Verhalten unlogisch und unnütz an der Stelle weil es zu Fehlern führt. Als Ausweg gleich in der Klasse oben MyRectangle bounds = new MyRectangle(); zu schreiben, nur damit bounds an der Stelle existiert, halte ich für kein gutes Design.

    greetz KN4CK3R



  • Du musst doch so oder so wissen, wie es ist. Die meisten würden erwarten, dass C++ das auch so macht. Dass es nicht so ist, ist ja eine künstliche Einschränkung, und kein natürliches Verhalten. C++ lässt den Programmierer meist tun, was er will, und überlässt ihm die Verantwortung. Aber hier werden die Möglichkeiten eingeschränkt, um eine mögliche Fehlerquelle auszuschließen und dafür eine neue zu erschaffen.



  • an der Stelle wird keine neue Fehlerquelle geschaffen, da Vererbung eine "ist ein" Beziehung ist und somit auch die Methode aus der Basisklasse funktionieren muss. Würde sie das nach dem Vererben nicht mehr tun, wäre es kein "ist ein" und damit wäre auch die Vererbung fehl am Platz.

    greetz KN4CK3R



  • Es gibt hier keine sinnvolle Frage.

    KN4CK3R schrieb:

    Warum ist das Verhalten in C# anders?

    Weil es eine andere Sprache ist.

    KN4CK3R schrieb:

    Von C++ bin ich es gewohnt ...

    für mich als C++ Programmierer ist das Verhalten unlogisch und unnütz an der Stelle weil es zu Fehlern führt

    Dann bleib doch einfach bei C++, wenn Dir C# so sehr missfällt.

    Man programmiert grundsätzlich verschieden in Java/C# einerseits und C++ andererseits. Beim Umstieg von C# auf C++ ist wahnsinnig viel Detailwissen über mögliche Fehlerquellen zu lernen.
    Ich neige langsam dazu zu behaupten, dass beim umgekehrten Umstieg von C++ nach C# kaum weniger zu lernen ist. C# ist syntaktisch einfacher und hat weniger Sonderfälle. Der Weg zu sauberem Code mit allen etablierten Pattern und Idiomen dürfte aber ebenso viel Zeit verschlingen.

    Das ist hochgradig subjektiv, aber ich kann mich Mechanics in dem Punkt anschließen, dass ich das Verhalten von C# in diesem konkreten Fall für logischer erachte.

    Edit: Tippfehler



  • Das ist eine Fehlerquelle, weil man normal erwarten würde, dass wenn man eine Methode aufruft, dann auch die überschriebene Methode aufgerufen wird. Das funktioniert ja auch, wenn du in einer Methode der Basisklasse eine virtuelle Methode aufrufst. Wenn du in einer Methode der Basisklasse die virtuelle Methode aufrufst, dann wird die Methode der abgeleiteten Klasse aufgerufen. Das ist nur im Konstruktor anders und das erwartet man normal nicht.


  • Administrator

    Auch in C++ soll man keine virtuellen Funktionen aus dem Konstruktor heraus aufrufen! Sowas wird meistens eher als schlechte Idee angeschaut. Weil man eben erwarten würde, dass die überschriebene Funktion aufgerufen wird. Such mal danach im Inet, wie viele Anfänger genau darüber gestolpert sind.

    Wieso rufst du hier auch überhaupt den Setter des Property auf? Wieso setzt du nicht gleich das Attribute location ? Wieso ist location überdies protected und nicht private ?

    KN4CK3R schrieb:

    Als Ausweg gleich in der Klasse oben MyRectangle bounds = new MyRectangle(); zu schreiben, nur damit bounds an der Stelle existiert, halte ich für kein gutes Design.

    Das wird auch nicht zum gewünschten Resultat führen. Die Konstruktoren der Basisklasse werden vorher aufgerufen. Das Problem liegt darin, dass du die falsche Funktion aufrufst, bzw. eben über das Property gehst.

    Grüssli



  • doch das geht, das wird noch vor dem BaseConstruktor aufgerufen. Der Code ist nur ein Beispiel, stell dir einfach vor dass da mehr passiert als nur einfach eine Zuweisung. Aber wenn das Verhalten in C# so gewünscht ist, dann soll mir das recht sein. 😉

    greetz KN4CK3R


  • Administrator

    KN4CK3R schrieb:

    doch das geht, das wird noch vor dem BaseConstruktor aufgerufen.

    Dann aus einem anderen Grund, als welcher du angegeben hast. Der Basiskonstruktor wird auf jedenfall vorher aufgerufen! Kannst du in der Dokumentation nachlesen oder gleich hier sehen:
    http://ideone.com/6LNsR

    Grüssli



  • Von C++ bin ich es gewohnt, dass der Construktor nur Funktionen aus seinem eigenen Scope aufruft und nicht die, die irgendwann einmal überschrieben werden von einer Unterklasse eben aus dem Grund, weil die Unterklasse zu diesem Zeitpunkt noch gar nicht existieren kann.
    Warum ist das Verhalten in C# anders?

    In C++ ist IMHO einfach das Problem, dass die vtable noch nicht fertig initialisiert sein kann, was bei C# nicht der Fall ist. Deshalb sagt der C# Compiler, du darfst das machen, musst dich aber selber drum kümmern, dass du keinen Blödsinn anstellst. Btw, wenn du das Visual Studio und den Resharper verwendest, weißt dieser dich afair auf das Prblem hin.


Anmelden zum Antworten