Hallo Harald!
Post by Harald M. GenauckPost by Herfried K. Wagner [MVP]Post by Harald M. GenauckPost by Herfried K. Wagner [MVP]Dass Konstruktoren nicht vererbt werden, ist keine sinnfreie
Einschränkung von .NET, sondern hat seine Ursache darin, dass eine
abgeleitete Klasse eventuell zusätzliche Daten zur Initialisierung
benötigt, also bestehende Konstruktoren für die
Instanzierung/Initialisierung nicht ausreichend sein können.
Das will mir als Grund aber nicht ganz einleuchten, zumindest nicht als
zwingender Grund. Denn wenn Konstruktoren vererbbar wären, könnten sie
ebenso sowohl überschreibbar als auch überladbar sein - und damit
durchaus die Möglichkeit für zusätzliche Initialisierungen und Parameter
für die Ableitung(en) anbieten...
Nehmen wir an, in einer Basisklasse ist ein Konstruktor 'New(A, B)'
definiert. Wenn die abgeleitete Klasse allerdings zur Initialisierung
/zwingend/ noch einen Wert 'C' benötigt, ist der Konstruktor 'New(A, B)'
unbrauchbar -- und wird aus dem Grund auch nicht geerbt. Selbst die
Möglichkeit, den Konstruktor zu überschreiben, hilft da wenig, denn der
Wert 'C' kann dadurch ja nicht durch den Benutzer der Klasse übergeben
werden.
Ich sehe immer noch keinen Unterschied zu "normalen" Methoden, bei denen
das gleiche Problem-Szenario ebenso auftreten kann.
Das Problem kann bei normalen Methoden in dieser Form nicht auftreten, da
die normalen Methoden ja in einer der Basisklassen implementiert sind und
aus dem Grund für sich funktionieren bzw. ihre Funktion bei der Basisklasse
spezifiziert ist und sich durch Vererbung (im Normalfall, sofern nicht
überschrieben/überschattet wird) nicht ändert. Überschriebene bzw.
überschattete Mitglieder sind dann aber klar als solche erkennbar, etwa im
Objektbrowser.
Jedes Objekt des Typs 'B' kann auch als Objekt des Typs 'A' gesehen werden,
wenn der Typ 'B' vom Typ 'A' erbt. Das impliziert, dass jedes Objekt des
Typs 'B' auch das kann, was ein Objekt vom Typ 'A' kann. Durch Überschreiben
kann es nicht zu ungewünschten Zuständen im Status eines Objekts gelangen,
da Überschreiben immer nur über eine Vererbungsebene möglich ist (d.h. ein
Überschreiben von 'MyBase.MyBase.Bla' ist nicht möglich).
Würden Konstruktoren geerbt werden, wäre jedoch genau dies möglich, d.h. es
kann zu inkonsistenten Objektzuständen kommen. Betrachten wir dazu folgendes
Beispiel:
\\\
' Das sollte die 'Object'-Klasse des Frameworks darstellen und ist hier
' nur zu Zwecken der besseren Verständlichkeit angeführt.
Public Class Object
Public Sub New()
...
End Sub
End Class
Public Class FileInfo
Inherits Object
Private m_FileName As String
Public Sub New(ByVal FileName As String)
m_FileName = FileName
End Sub
Public ReadOnly Property FileName() As String
Get
Return m_FileName
End Get
End Class
Public Class SpecialFieldInfo
Inherits FieldInfo
Public Sub New()
MyBase.New("C:\AUTOEXEC.BAT")
End Sub
Public Sub New(ByVal FileName As String)
MyBase.New(FileName)
End Sub
End Class
///
Obenstehender Code definiert die Klasse 'FileInfo', die von 'Object' erbt,
sowie eine Klasse 'SpecialFileInfo', die 'FileInfo' erweitert.
Nehmen wir nun an, Konstruktoren könnten vererbt werden. Dann würde
'FileInfo', wie 'Object', über einen parameterlosen Konstruktor verfügen.
Nehmen wir weiters an, dass die Dokumentation zur Eigenschaft 'FileName' der
Klasse 'FileInfo' besagt, dass der bei der Erstellung der Klasse angegebene
Dateiname zurückgegeben wird und ausserdem die weitere interne Logik der
Klasse 'FileInfo' nur dann funktioniert, wenn bereits im Konstruktor der
Klasse 'm_FileName' auf einen Pfad gesetzt wurde. Instanziert nun der
Benutzer der Klasse 'FileInfo' mit dem von 'Object' geerbten, parameterlosen
Konstruktor, kann das zuvor Gesagte nicht mehr sichergestellt werden.
'SpecialFileInfo' besitzt zwei Konstruktoren, einen parameterlosen und einen
parametrisierten. Da in 'FileInfo' kein parameterloser Konstruktor vorhanden
ist (und damit festgelegt wird, dass jede Ableitung der Klasse einen
Dateinamen übergeben muss, um die Funktionalität der Klasse
sicherzustellen), müssen alle in 'SpecialFileInfo' vorhandenen Konstruktoren
den Konstruktor der Basisklasse aufrufen. Dadurch wird garantiert, dass
diese auch problemlos initialisiert werden kann. Würde, weil Konstruktoren
geerbt werden, weiterhin der parameterlose Konstruktor aus 'Object' auch in
'SpecialFileInfo' vorhanden sein, könnte nicht garantiert werden, dass sich
Objekte des Typs 'FileInfo' (und davon abgeleiteten Typen) immer in einem
konsistenten Zustand befinden.
Post by Harald M. GenauckPost by Herfried K. Wagner [MVP]Allenfalls könnte man eine Ausnahme werfen,
...was man bei "normalen" Methoden ja genau dann tun muss, weil sie eben
vererbt werden.
Tut man aber im Normalfall nicht, zumindest sind mir noch keine solchen
Fälle begegnet -- und selbst wenn, würde ich dies in den meisten Fällen als
Indikator für ein unsauberes Klassendesign werten.
Post by Harald M. GenauckOder gibt es eine Möglichkeit, einer Ableitung eine geerbte Methode
"wegzunehmen", d.h. sie auszublenden und gegen Verwendung zu sperren? Ich
behelfe mir bisher immer damit, in der Ableitung eine nicht verwendbare
Basis-Methode als "Private Overrides..." zu deklarieren. Das erscheint mir
zwar wenig elegant, funktioniert aber offensichtlich soweit, so lange die
Ableitung nicht auf den Basis-Typ gecastet wird...
Etwas wegzunehmen, was bereits da ist, widerspricht Vererbung als Mittel zur
Erweiterung. Was die Basisklasse kann, kann auch die abgeleitete Klasse.
Deshalb gibt es auch keine direkten Mittel, die das Entfernen von Geerbtem
zur "Standardvorgehensweise" machen. Allenfalls ist in Frage zu stellen, ob
die Ableitung überhaupt sinnvoll ist, wenn Funktionalität der Basisklasse
nicht weiter bereitgestellt werden soll.
--
M S Herfried K. Wagner
M V P <URL:http://dotnet.mvps.org/>
V B <URL:http://dotnet.mvps.org/dotnet/faqs/>