PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Erste Gehversuche in der Vererbung (C#)


Pava
2008-10-07, 20:36:09
Hallo miteinander,

zurzeit versuche ich mich mit dem Thema Vererbung auseinanderzusetzen, bin dabei mäßig erfolgreich. Ich hatte zuvor noch nie mit Vererbung zu tun, auch in keiner anderen Sprache als C#. Ich konnte mithilfe von Google schon eine Hand voll Fragen lösen, jedoch steck ich gerade fest.

Ich habe eine abstrakte Klasse mit diversen Feldern erstellt und diese als protected markiert. Eigentlich müssten alle erbenden Klassen doch jetzt auch diese Felder haben, oder etwa nicht? Afaik ist ja 'protected' dafür da.
Leider zeigt mir Intellisense bei den erbenden Klassen keine Felder aus der Basisklasse an, wenn ich manuell zugreife gibts Fehler beim Kompilieren.


public abstract class Item
{
protected string name;
}

public class PickUp : Item
{
// wie kann man hier name benutzen?
}


Was muss ich also tun, um an diese ranzukommen?

---

Man kann mit abstrakten Methoden ja schön festlegen, welche Methoden erbende Klassen enthalten MÜSSEN.
Die selbe Aufgabe erledigen afaik auch Interfaces.

Bei den Collections z.B. haben arraylist, hashtable etc. ja alle die Methode Add(), weil sie eben diese Schnittstelle implementieren und der Programmierer muss nur einmal die Schnittstelle lernen, um mit allen Collections arbeiten zu können.
Doch das Ganze könnte man doch auch mit abstrakten Klassen/Methoden hinbekommen.. Sind Interfaces neben den abstrakten Methoden jetzt eigentlich überflüssig, oder können die noch was anderes tolles, was abstrakte Klassen/Methoden nicht können? Mir ist dier Sinn noch nicht ganz klar geworden.

---

Kann mir das mit den Konstruktoren und der Vererbung nochmal jemand verständlich zusammenfassen?
Ich weiß, dass Konstruktoren nicht vererbt werden, weil die Kindklasse vermutlich erweitert wird und der Konstruktor der Basisklasse da zu mager wäre, um den Anforderungen der Kindklasse zu genügen.
Also muss man für jede abgeleitete Klasse neue Konstruktoren definieren.
Doch da gibts doch eine Schwierigkeit, dass trotzdem zuerst der Konstruktor der Basisklasse ausgeführt wird, selbst wenn man in der Kindsklasse einen parametrisierten Konstruktor hat, und dann das Schlüsselwort base etc.. alles irgendwie unübersichtlich für mich.


---


Es wäre echt super, wenn ihr nicht alles in fachchinesisch erläutern würdet, sondern so, dass es ein Anfänger auch verstehen kann.

Danke!

GamlerHart
2008-10-07, 22:35:38
public abstract class Item
{
protected string name;
}

public class PickUp : Item
{
// wie kann man hier name benutzen?
void Test()
{
name = "fun";
}
}


Das müsste einfach gehen. Weis nicht warum IntelliSense das nicht bringt.

Gast
2008-10-07, 23:02:13
Sind Interfaces neben den abstrakten Methoden jetzt eigentlich überflüssig, oder können die noch was anderes tolles, was abstrakte Klassen/Methoden nicht können? Mir ist dier Sinn noch nicht ganz klar geworden.


Also erst einmal wäre festzuhalten, dass eine abstrakte Klasse vom Umfang trotzdem eine Klasse ist. D.h. neben weiteren Interfaces kann die erbende Klasse nicht von einer anderen Klasse als die abstrakte Klasse erben.
D.h. wiederum im Umkehrschluss, dass der Ersteller der abstrakten Klasse sich sicher sein kann, dass die abgeleitete Klasse von keiner weiteren Klasse erben kann. Deswegen eignet sich die abstrakte Klasse prima als Basisklasse, da sie nicht für sich alleine instanziert werden kann.

Eine abstrakte Klasse kann auch von einer normalen nicht-abstrakten Klasse abgeleitet sein oder Interfaces implementieren. Du kannst sogar in einer abgeleiteten abstrakten Klasse einen virtual Member (der kann auch schon implementiert sein) der Basisklasse wieder als abstract definieren oder ihnen versiegeln (dann ist er nicht mehr virtual).

Die Möglichkeiten sind schon deutlich höher als bei Interfaces alleine. Aber du hast insofern Recht, dass es wenig Sinn macht, abstrakte Klassen lediglich auf Interface Niveau zu verwenden. Abstrakte Klassen sollte man immer dann verwenden, wenn eine Teil Implementierung vorhanden ist und weitervererbt werden soll und ein anderer Teil vom Implementierer der abstrakten Klasse implementiert werden soll.


Also muss man für jede abgeleitete Klasse neue Konstruktoren definieren.


Explizit definieren musst du ihn nur, wenn deine Basisklasse einen parametrisierten Konstruktor enthält. Beim Instanzieren der abgeleiteten Klasse muss es ja die Möglichkeit geben, den überladenen Konstruktor der Basisklasse aufzurufen. Deswegen muss dann der neu implementierte parametrisierte Konstruktor die Parameter auch zum Kontruktor der Basisklasse weiterrouten.

Enthält die Basisklasse aber keinen überladenen Kontruktor musst du in der abgeleiteten Klasse den Konstruktor nicht explizit angeben, wenn du da nichts machen willst. Das macht dann der Compiler automatisch für dich. Nur bei structs ist das etwas anderes, da wird kein Standardkonstruktor erstellt.


Doch da gibts doch eine Schwierigkeit, dass trotzdem zuerst der Konstruktor der Basisklasse ausgeführt wird...

Das ist ja auch richtig so und wäre schlimm, wenn es anders wäre, denn die Basisfunktionalität soll ja erhalten bleiben. Es kann ja sein, dass der Kontruktor der Basisklasse irgend eine Methode aufruft und Dinge abarbeitet.

Bietchiebatchie
2008-10-07, 23:15:30
Hallo miteinander,

zurzeit versuche ich mich mit dem Thema Vererbung auseinanderzusetzen, bin dabei mäßig erfolgreich. Ich hatte zuvor noch nie mit Vererbung zu tun, auch in keiner anderen Sprache als C#. Ich konnte mithilfe von Google schon eine Hand voll Fragen lösen, jedoch steck ich gerade fest.


Kleiner (prinzipieller) Tip: gib doch außerdem an in welcher/welchen Sprache/n du schon gearbeitet hast - das kann evtl. hilfreif sein um z.B. Konstrukte zu erklären die in den von dir bekannten Sprachen einen anderen Namen haben o.ä. ;)


Ich habe eine abstrakte Klasse mit diversen Feldern erstellt und diese als protected markiert. Eigentlich müssten alle erbenden Klassen doch jetzt auch diese Felder haben, oder etwa nicht? Afaik ist ja 'protected' dafür da.
Leider zeigt mir Intellisense bei den erbenden Klassen keine Felder aus der Basisklasse an, wenn ich manuell zugreife gibts Fehler beim Kompilieren.


Doch du kannst innerhalb von allen Methoden auf die protected-Member zugreifen - VS zeigt das afaik auch immer korrekt an.
Also z.B.


public abstract class Item
{
protected string name;
}

public class PickUp : Item
{
public void GetName()
{
return name;
}
}



Man kann mit abstrakten Methoden ja schön festlegen, welche Methoden erbende Klassen enthalten MÜSSEN.
Die selbe Aufgabe erledigen afaik auch Interfaces.

Bei den Collections z.B. haben arraylist, hashtable etc. ja alle die Methode Add(), weil sie eben diese Schnittstelle implementieren und der Programmierer muss nur einmal die Schnittstelle lernen, um mit allen Collections arbeiten zu können.
Doch das Ganze könnte man doch auch mit abstrakten Klassen/Methoden hinbekommen.. Sind Interfaces neben den abstrakten Methoden jetzt eigentlich überflüssig, oder können die noch was anderes tolles, was abstrakte Klassen/Methoden nicht können? Mir ist dier Sinn noch nicht ganz klar geworden.


Es gibt ein paar prinzipielle Unterschiede zwischen Interfaces und abstrakten Klassen:
die Methoden/Properties/Events in Interfaces sind immer automatisch public - bei abstrakten Methoden können alle normalen (außer private) Zugriffsrestriktoren benutzt werden. Jede Klasse kann nur genau eine Oberklasse haben, aber beliebig viele Interfaces implementieren. allgemein kann man sagen: eine abstrakte Klasse gibt deutlich genauere Auskunft was ein Objekt genau ist, während eine Interface nur spezifiziert wie es sich verhalten kann - Beispiel ICollection: Es kann sein, dass das eine normale List<T> ist - es kann aber auch sein dass sich dahinter ein Zugriff auf eine Datenbank auf einem anderen Rechner verbirgt.

D.h. wie du schon angemerkt hast im Grunde kann man alles was man mit Interfaces erreichen kann auch mit abstrakten Klassen erreichen - Interfaces sind aber einfacher.

---


Kann mir das mit den Konstruktoren und der Vererbung nochmal jemand verständlich zusammenfassen?
Ich weiß, dass Konstruktoren nicht vererbt werden, weil die Kindklasse vermutlich erweitert wird und der Konstruktor der Basisklasse da zu mager wäre, um den Anforderungen der Kindklasse zu genügen.
Also muss man für jede abgeleitete Klasse neue Konstruktoren definieren.
Doch da gibts doch eine Schwierigkeit, dass trotzdem zuerst der Konstruktor der Basisklasse ausgeführt wird, selbst wenn man in der Kindsklasse einen parametrisierten Konstruktor hat, und dann das Schlüsselwort base etc.. alles irgendwie unübersichtlich für mich.


Ich denke ein paar Beispiele zeigen die Möglicheiten auf:

public abstract class Employee
{
protected Employee()
{
this.name = "no name";
}

protected Employee(string name)
{
this.name = name;
}

private string name;
}

public class Codemonkey : Employee
{
public Codemonkey()
// hier wird implizit der parameterlose Konstruktor Employee() aufgerufen
{
}
}

public class Boss : Employee
{
public Boss()
: base("bla")
{
}

public Boss(string name)
: base(name)
{
}
}

Pava
2008-10-07, 23:22:24
public abstract class Item
{
protected string name;
}

public class PickUp : Item
{
// wie kann man hier name benutzen?
void Test()
{
name = "fun";
}
}


Das müsste einfach gehen. Weis nicht warum IntelliSense das nicht bringt.

Das geht auch! Wenn ich das aber außerhalb der Methode Test machen will gehts plötzlich nicht mehr. Was ist der Grund? Felder sollten doch überall in der Klasse zu erreichen sein.

Danke auch an den Gast, werde morgen nochmal was zu Fragen haben :)

Bietchiebatchie
2008-10-07, 23:37:15
Das geht auch! Wenn ich das aber außerhalb der Methode Test machen will gehts plötzlich nicht mehr. Was ist der Grund? Felder sollten doch überall in der Klasse zu erreichen sein.
Naja, innerhalb jeder/s Konstruktors, Property, Methoden, Events kannst du auf die protected Sachen zugreifen.
Du kannst NICHT bei direkt initilisierten Werten auf die Oberklassen-Variablen zugreifen; z.B.

public class Foo1
{
protected string bar1;
}

public class Foo2 : Foo1
{
// das geht nicht, da auf bar1 kein Zugriff.
private string bar2 = bar1;
}
Ich glaube kaum, dass der Grund dieses Verhaltens für dich relevant ist aber trotzdem: bei einer Objekterzeugung wird zuerst der Basisklassenkonstruktor aufgerufen bevor der aktuelle Konstruktor ausgeführt wird. das hat zur Folge dass alle membervariablen der Basisklasse korrekt initialisiert sind und deshalb darf man auf sie zugreifen.
Vor den Konstruktoraufrufen werden alle direkt initialisierten Werte allerdings in umgekehrter Reihenfolge initialisiert - d.h. zuerst die Werte der abgeleiteten Klassen dann erst die Werte der Oberklasse. Daher sind logischerweise die (noch nicht initialisierten) Werte der Basisklasse unsichtbar.

Pava
2008-10-07, 23:38:27
Hey Bietchiebatchie

Kleiner (prinzipieller) Tip: gib doch außerdem an in welcher/welchen Sprache/n du schon gearbeitet hast - das kann evtl. hilfreif sein um z.B. Konstrukte zu erklären die in den von dir bekannten Sprachen einen anderen Namen haben o.ä. ;)


Nur PHP5, aber da ist ja OOP nur sehr beschränkt verwendbar


Doch du kannst innerhalb von allen Methoden auf die protected-Member zugreifen - VS zeigt das afaik auch immer korrekt an.
Also z.B.


public abstract class Item
{
protected string name;
}

public class PickUp : Item
{
public void GetName()
{
return name;
}
}


Okay, wie schon erwähnt, das geht. Aber warum nur innerhalb von Methoden? Wahrscheinlich steh ich gerade auf dem Schlauch und das ganze macht anders gar keinen Sinn.

Aber angenommen, ich habe wie oben Item als Basisklasse und will nun mit PickUps konkrete Dinge ableiten, welche einen festen, hardcodierten Namen besitzen. Da bräuchte man doch eigentlich keine Methode dafür.. in der Basisklasse gehts schliesslich auch ohne, wenn man das Feld nicht nur deklariert, sondern auch gleich selbst initialisiert.

Pava
2008-10-07, 23:39:50
Da hast du die Antwort gegeben, bevor ich die Frage gestellt hab, danke nochmals.

Andmar
2008-10-08, 10:02:11
Falls Du es nicht kennst: http://openbook.galileocomputing.de/visual_csharp/ . Ein sehr einsteigerfreundliches Buch.