Vererbung
Was ist Vererbung?
Da die Entwicklung einer Klasse von Grund auf sehr aufwändig sein kann, kann statt dessen
eine bestehende Klasse wiederverwendet und erweitert werden. Dieses Verfahren, das als
Vererbung bezeichnet wird, erzeugt aus einer bestehenden Klasse - der sogenannten
Basisklasse - eine neue Klasse - die sogenannte abgeleitete Klasse - , die über alle Felder,
Eigenschaften und Methoden der Basisklasse verfügt und diese um eigene Elemente erweitern
kann.
Vererbung wird in C# mit Hilfe des Operators : ausgedrückt, wobei dieser sowie der Name
der Basisklasse dem Namen der abgeleiteten Klasse nachgestellt werden. Es wurde bereits
der Typ
object erwähnt, von dem jeder Typ ableitet. Dies kann nun präzisiert werden: Wird
für eine Klasse nicht explizit eine Basisklasse angegeben, leitet sie implizit von
object
ab. Das heißt, dass alle Eigenschaften und Methoden, die für
object definiert sind, auch
in dieser Klasse zur Verfügung stehen.
Potenziell kann eine Klasse auch explizit von
object abgeleitet werden, indem
object als
Basisklasse angegeben wird. Da dies auf Grund der impliziten Ableitung von
object aber
keinen Unterschied macht, wird diese Ableitung in der Regel nicht angegeben.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a foo class that explicitly derives from
/// object.
/// </summary>
public class Foo : object
{
}
}
|
ist also äquivalent zu
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a foo class that implicitly derives from
/// object.
/// </summary>
public class Foo
{
}
}
|
Ein Beispiel für eine Methode, die implizit in allen Typen enthalten ist, ist die Methode
ToString. Sie dient dazu, einen
string zurückzugeben, der eine für Menschen lesbare
Repräsentation des Objekts darstellt. Die in dem Typ
object definierte Methode kann
zwar an einem Objekt einer beliebigen Klasse aufgerufen werden, allerdings kennt sie die
spezifischen Details der Klasse nicht. Daher gibt diese Methode standardmäßig den
vollqualifizierten Typ des Objekts zurück, an dem sie aufgerufen wird.
Wird ein Objekt vom Typ ComplexNumber, der im vorangegangenen Kapitel entwickelt wurde,
instanziiert, gibt die Methode ToString beispielsweise
GoloRoden.GuideToCSharp.ComplexNumber
zurück. Um eine spezifische Version der Methode ToString für den Typ ComplexNumber zu
erzeugen, muss diese Methode der Klasse ComplexNumber hinzugefügt werden. Im Gegensatz
zu einer klassischen Methodendefinition muss dieser Definition zwischen dem
Zugriffsmodifizierer und dem Typ des Rückgabewertes das Schlüsselwort
override hinzugefügt
werden, um sicherzustellen, dass das Überschreiben der Methode der Basisklasse nicht
aus Versehen, sondern absichtlich geschieht.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a complex number.
/// </summary>
public class ComplexNumber
{
#region Properties
#endregion
#region Methods
// ...
/// <summary>
/// Gets a string representation of the current
/// instance.
/// </summary>
/// <returns>A string representation of the current
/// instance.</returns>
public override string ToString()
{
// TODO gr: Create the string representation and
// return it to the caller.
// 2007-06-11
}
#endregion
#region Constructors
#endregion
}
}
|
Wird das Schlüsselwort
override weggelassen, meldet der Compiler beim Übersetzen der
Anwendung eine entsprechende Warnung und fordert den Entwickler auf, das fehlende
Schlüsselwort zu ergänzen.
Eine wesentliche Eigenschaft in der objektorientierten Programmierung ist in diesem
Zusammenhang die Polymorphie, also die Fähigkeit eines Objekts, je nach Kontext
verschiedenen Typen zu entsprechen. Jeder Typ kann durch einen übergeordneten und
damit allgemeineren Typ repräsentiert werden, da dieser eine Generalisierung
darstellt.
In der Praxis heißt das, dass jeder Methode, die beispielsweise einen Parmeter vom
Typ
object erwartet, ein Objekt eines beliebigen Typs übergeben werden kann - da
jeder Typ implizit von
object abgeleitet ist und
object damit eine Generalisierung
dieses Typs darstellt. Umgekehrt funktioniert dies allerdings nicht: Wird ein
Parameter eines bestimmten Typs erwartet, können nur Objekte dieses oder eines
abgeleiteten Typs übergeben werden.
Dieses System der Generalisierung und Spezialisierung ist ein Kernkonzept der
objektorientierten Programmierung und stellt auch den Grund dar, warum jeder Typ
mit Hilfe von Boxing in
object umgewandelt werden kann - intern wird hier auf
Polymorphie zurückgegriffen.
Im Allgemeinen gilt für die Beziehung zwischen einem Typ und seiner Basisklasse
eine "is a"-Beziehung: Jedes Objekt vom Typ ComplexNumber ist gleichzeitig auch
vom Typ
object, während ein abgeleiteter Typ von ComplexNumber sogar zugleich vom
Typ ComplexNumber und vom Typ
object ist.
Allerdings erfordert diese Beziehung bei der Modellierung der Klassenhierarchie
mehr Aufmerksamkeit, als sie zunächst vermuten lässt. Der Grund hierfür liegt
in einer wesentlichen Forderung der objektorientierten Programmierung, die von
Barbara Liskov formuliert wurde und daher als Liskov-Prinzip bezeichnet wird.
Die Forderung besagt, dass das Verhalten einer abgeleiteten Klasse und das
ihrer Basisklasse identisch sein müssen.
Dies bedeutet, dass entgegen dem umgangssprachlichen Gebrauch ein Quadrat kein
Rechteck ist, weshalb eine Klasse zur Modellierung von Quadraten nicht von
einer Klasse zur Modellierung von Rechtecken abgeleitet werden darf. Während
die Höhe und die Breite eines Rechtecks unabhängig voneinander verändert werden
können, ist dies bei einem Quadrat nicht möglich.
Angenommen, ein Typ Quadrat wäre abgeleitet von einem Typ Rechteck, dann könnte
auf Grund der Polymorphie und der Generalisierung in jeder Methode, die ein Objekt
vom Typ Rechteck als Parameter erwartet, auch ein Objekt vom Typ Quadrat übergeben
werden. Diese Methode könnte eine Seite dieses Objektes verdoppeln, wodurch sich
bei einem Objekt des Typs Rechteck der Flächeninhalt ebenfalls verdoppelt.
Wird statt dessen ein Objekt vom Typ Quadrat übergeben, gilt dies nicht - hier
würde sich der Flächeninhalt vervierfachen, da die beiden Seiten nicht unabhängig
voneinander verändert werden können. Weil dabei das Liskovsche Prinzip verletzt
wird, ist diese Ableitung fehlerhaft.
Außer der bislang genannten Vererbung, bei der eine Klasse von genau einer
Basisklasse ableitet, gibt es prinzipiell auch die Mehrfachverebung, bei der
eine Klasse über mehrere Basisklassen verfügen kann. Dieses Konzept wird in C#
allerdings nicht unterstützt, da Mehrfachvererbung unter Umständen keine eindeutigen
Ableitungen erzeugt, und der Nutzen in keinem Verhältnis zu dem nötigen Aufwand
und der hohen Komplexität steht.
Strukturen können im Gegensatz zu Klassen nicht vererbt werden.
Felder und Eigenschaften
Die einfachsten Elemente eines Typs, die vererbt werden können, sind Felder. Bisher
wurden Felder in der Regel als
private gekennzeichnet, um den direkten Zugriff von
außerhalb der Klasse zu verhindern. Allerdings kann auf solche Felder auch aus einer
Unterklasse nicht zugegriffen werden. Um dies in einem gegebenen Fall zu ermöglichen,
gibt es verschiedene Alternativen.
Die einfachste Variante besteht darin, das Feld als
internal oder gar als
public zu
kennzeichnen. Allerdings geht dabei der Zugriffsschutz von außerhalb der Klasse
verloren, was der Objektorientierung in den meisten Fällen widerspricht. Eine andere
Möglichkeit besteht darin, über die entsprechende Eigenschaft indirekt auf das Feld
zuzugreifen, was eine im Hinblick auf die Objektorientierung deutlich sauberere
Variante darstellt.
In der Praxis verfügt aber nicht jedes Feld über eine zugehörige Eigenschaft, da
in der Regel nur solche Felder mit einer Eigenschaft ausgestattet werden, die für
die Konfiguration eines Objektes von außen wichtig sind. Felder, die hingegen nur
für interne Berechnungen oder sonstige interne Belange genutzt werden und außerhalb
eines Objektes nicht zugreifbar sein sollen, bleiben üblicherweise ohne entsprechende
Eigenschaft.
Abhilfe schafft in einem solchen Fall das Schlüsselwort
protected, das den Zugriff
nicht nur aus der Klasse, welche die Felddefinition enthält, ermöglicht, sondern auch
aus jeder Unterklasse dieser Klasse. Felder, die als
protected gekennzeichnet sind,
stehen also von der Ebene des Zugriffs zwischen
public und
private.
Außerdem gibt es noch die Erweiterung des Schlüsselwortes
protected auf
protected
internal, wodurch der Zugriff ebenfalls aus abgeleiteten Klassen ermöglicht wird,
allerdings nur, sofern diese sich innerhalb der gleichen Assembly befinden.
Methoden
Werden Methoden in einem abgeleiteten Typ überschrieben, muss in dem abgeleiteten
Typ die Methode explizit als
override gekennzeichnet werden, um anzuzeigen, dass
das Überschreiben beabsichtigt und kein Versehen ist. Allerdings kann nicht jede
beliebige Methode einer Basisklasse überschrieben werden - dort muss eine Methode
zunächst als überschreibbar gekennzeichnet werden. Dies geschieht mit Hilfe des
Schlüsselwortes
virtual.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a base class.
/// </summary>
public class BaseClass
{
/// <summary>
/// Represents a virtual foo method.
/// </summary>
public virtual void Foo();
}
}
|
Methoden, die nicht als
virtual gekennzeichnet werden, können
von abgeleiteten Typen nicht überschrieben werden. Damit eine Methode mit dem Schlüsselwort
virtual markiert werden kann, darf sie nicht mit dem
Zugriffsmodifizierer
private markiert sein - da sie in diesem Fall in dem abgeleiteten Typ
nicht sichtbar ist. Zudem kann
virtual nicht gleichzeitig
mit
override angegeben werden.
Wird während der Ausführung einer Anwendung eine virtuelle Methode aufgerufen,
ermittelt die Common Language Runtime den tatsächlichen Typ des Objektes, an dem
die Methode aufgerufen wird und ruft die zugehörige Methode auf - falls eine
entsprechende überschriebene Variante verfügbar ist. Auf diese Art wird gewährleistet,
dass für ein Objekt immer die korrekte Version einer Methode aufgerufen wird.
Außer
override gibt es noch das Schlüsselwort
new. Der Unterschied liegt in der
Bindung der Methode an den Typ - bei
override wird die Methode in jedem Fall für
den zugehörigen Typ aufgerufen, da die Methode der Basisklasse überschrieben wurde,
bei
new wird die Methode unter Umständen für den Basistyp aufgerufen, da diese
Methode nicht überschrieben, sondern nur ausgeblendet wurde.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a base class.
/// </summary>
public class BaseClass
{
/// <summary>
/// Represents a virtual foo method.
/// </summary>
public virtual void Foo()
{
}
}
/// <summary>
/// Represents a class that derives from BaseClass.
/// </summary>
public class DerivedClassA : BaseClass
{
/// <summary>
/// Represents a foo method that overwrites the base
/// class's implementation.
/// </summary>
public override void Foo()
{
}
}
/// <summary>
/// Represents another class that derives from
/// BaseClass.
/// </summary>
public class DerivedClassB : BaseClass
{
/// <summary>
/// Represents a foo method that shadows the base
/// class's implementation.
/// </summary>
public new void Foo()
{
}
}
}
|
Beispielhaft lässt sich das an der Klasse ComplexNumber verdeutlichen. Die Methode
ToString ist dort als
override gekennzeichnet. Das heißt, wird die Methode ToString
an einem Objekt dieser Klasse aufgerufen, dann wird der Code ausgeführt, der in
der überschriebenen Methode definiert wurde. Dieser Code wird auch dann ausgeführt,
wenn das Objekt beispielsweise als
object geboxt wird.
Wäre die Methode ToString statt dessen als
new gekennzeichnet, würde ebenfalls
der in der Klasse ComplexNumber definierte Code ausgeführt - aber nur, wenn diese
Methode an dem ungeboxten Objekt aufgerufen wird. Erfolgte der Aufruf statt dessen
an einer geboxten Version des Objektes, so würde der Code des geboxten Typs
ausgeführt.
Würde das Objekt also als
object geboxt, würde bei einem Aufruf der Methode
ToString die Version ausgeführt, die in der Klasse
object definiert wurde. In der
Praxis wird
new allerdings eher selten verwendet, in der Regel kommt das
Schlüsselwort
override zum Einsatz.
In einigen Fällen soll aus einer überschriebenen Methode explizit die Methode der
Basisklasse aufgerufen werden, zum Beispiel, um deren Funktionalität auch in der
überschreibenden Methode nutzen zu können. Dazu dient das Schlüsselwort
base, das
analog zu
this verwendet werden kann, allerdings statt auf das eigene Objekt immer
auf den Typ der Basisklasse verweist.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a base class.
/// </summary>
public class BaseClass
{
/// <summary>
/// Represents a virtual foo method.
/// </summary>
public virtual void Foo()
{
}
}
/// <summary>
/// Represents a class that derives from BaseClass.
/// </summary>
public class DerivedClass : BaseClass
{
/// <summary>
/// Represents a foo method that overwrites the base
/// class's implementation.
/// </summary>
public override void Foo()
{
// Call the base method.
base.Foo();
}
}
}
|
Prinzipiell kann eine Methode, die mit
override oder
new gekennzeichnet wurde, in
einer weiteren abgeleiteten Klasse wiederum überschrieben werden. Das Schlüsselwort
virtual bezieht sich also nicht nur auf die direkt nachfolgende
Ableitung, sondern auf alle Klassen, die in der Ableitungshierarchie nachfolgen. Um dies
zu verhindern und eine weitere Vererbung zu verhindern, kann eine Methode, die mit
override oder
new gekennzeichnet wurde, mit Hilfe des Schlüsselwortes
sealed
versiegelt werden, wodurch keine weitere Überschreibung dieser Methode mehr möglich ist.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a base class.
/// </summary>
public class BaseClass
{
/// <summary>
/// Represents a virtual foo method.
/// </summary>
public virtual void Foo()
{
}
}
/// <summary>
/// Represents a class that derives from BaseClass.
/// </summary>
public class DerivedClass : BaseClass
{
/// <summary>
/// Represents a foo method that overwrites the base
/// class's implementation and avoids any further
/// overwriting by sealing this method.
/// </summary>
public override sealed void Foo()
{
}
}
}
|
Außerdem können vollständige Klassen versiegelt werden, was bedeutet, dass eine
solche Klasse nicht vererbt werden kann. Dies ist bei Klassen sinnvoll, die eine
feststehende Funktionalität bereitstellen, wie beispielsweise Klassen mit
mathematischen Methoden - eine Methode zur Berechnung der Sinusfunktion zu
überschreiben, ergibt wenig Sinn, schließlich ist der Sinus bereits das endgültige
Resultat.
Daher ist beispielsweise die von .NET bereitgestellte Klasse Math im Namensraum
System versiegelt, ebenso kann die Klasse ComplexNumber versiegelt werden.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a complex number.
/// </summary>
public sealed class ComplexNumber
{
#region Properties
#endregion
#region Methods
#endregion
#region Constructors
#endregion
}
}
|
Unabhängig davon, ob die Klasse ComplexNumber versiegelt ist oder nicht, handelt
es sich um eine konkrete Klasse. Das bedeutet, dass sie instanziiert werden kann,
dass also Objekte von ihr erzeugt werden können.
Manchmal kann es sinnvoll sein, statt dessen eine sogenannte abstrakte Klasse zu
erzeugen, die nicht instanziiert werden kann, die nur als Basisklasse für andere
Klassen genutzt wird, um beispielsweise gemeinsam genutzte Funktionalität zentral
zur Verfügung zu stellen. Eine solche Klasse wird mit dem Schlüsselwort
abstract gekennzeichnet und kann nicht versiegelt werden.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents an abstract base class.
/// </summary>
public abstract class AbstractBaseClass
{
/// <summary>
/// Represents a virtual foo method.
/// </summary>
public virtual void Foo()
{
}
}
}
|
In einer abstrakten Klasse können zudem abstrakte Methoden definiert werden, die
keinen Methodenrumpf enthalten, sondern nur aus dem Methodenkopf bestehen. Solche
Methoden müssen mit dem Schlüsselwort
abstract versehen
werden und sind implizit
virtual. Statt eines Methodenrumpfes,
der in geschweiften Klammern angegeben wird, wird deren Methodenkopf mit einem Semikolon
abgeschlosen.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents an abstract base class.
/// </summary>
public abstract class AbstractBaseClass
{
/// <summary>
/// Represents an abstract foo method.
/// </summary>
public abstract void Foo();
}
/// <summary>
/// Represents a class that derived from
/// AbstractBaseClass.
/// </summary>
public class DerivedClass : AbstactBaseClass
{
/// <summary>
/// Represents a method that implements the base
/// class's abstract method.
/// </summary>
public override void Foo()
{
// TODO gr: Implement abstract method.
// 2008-01-03
}
}
}
|
In einer abgeleiteten Klasse müssen abstrakte Methoden in jedem Fall implementiert
werden, es sei denn, die abgeleitete Klasse wird ihrerseits wiederum als
abstract gekennzeichnet.
Gelegentlich kann es notwendig sein, eine bestehende Klasse ohne Erzeugung einer
abgeleiteten Klasse zu erweitern, ohne allerdings Zugriff auf ihren Quelltext zu haben.
Beispielsweise würde eine Erweiterung des Typs
string diesem
Vorhaben entsprechen.
Zu diesem Zweck gibt es seit der Version 3.0 von C# sogenannte Erweiterungsmethoden, mit
denen vorhandene Typen ergänzt werden können. Da diese Möglichkeit äußerst mächtig ist und
schnell zu unübersichtlichem Code führt, wird ihr Einsatz in der Praxis als schlechter Stil
angesehen. Dass Erweiterungsmethoden in C# 3.0 überhaupt in Erscheinung treten, gründet sich in
der Abfragetechnik Linq, die mit C# 3.0 eingeführt wurde und auf Erweiterungsmethoden
basiert.
Um einen bestehenden Typ zu erweitern, wird innerhalb einer statischen Klasse eine
statische Methode definiert, welche die entsprechende Funktionalität bereitstellt. Als
erster Parameter wird dieser Methode der zu erweiternde Typ übergeben, allerdings ergänzt
um das Schlüsselwort
this, woran C# erkennen kann, dass es sich
nicht um eine normale, sondern um eine Erweiterungsmethode handelt.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Contains extension methods.
/// </summary>
public static class ExtensionMethods
{
/// <summary>
/// Converts the specified string to its XML
/// representation.
/// </summary>
/// <param name="source">The string that shall be
/// converted to XML.</param>
/// <returns>The XML representation of the specified
/// string.</returns>
public static string ToXml(this string source)
{
// TODO gr: Transform the source string to XML and
// return the result to the caller.
// 2007-12-26
}
}
}
|
Die auf diese Art definierte Erweiterungsmethode für den Typ
string
kann nun an jeder Zeichenkette aufgerufen werden, als ob sie eine vordefinierte Methode
wäre.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a foo string.
string foo = "Hello world!";
// Get the XML representation of the string.
string xml = foo.ToXml();
}
}
}
|
Intern prüft C# beim Aufruf einer Methode zunächst, ob eine entsprechende Methode an dem
jeweiligen Typ definiert ist. Wenn nicht, wird überprüft, ob es eine statische Methode
innerhalb einer statischen Klasse gibt, deren Name dem der aufgerufenen Methode entspricht,
und deren erster Parameter dem gewünschten Typ entspricht, der außerdem mit dem Schlüsselwort
this gekennzeichnet wurde. Falls eine solche Methode existiert, wird
diese ausgeführt, andernfalls wird ein Fehler gemeldet.
Konstruktoren
Die einzigen Elemente eines Typs, die nicht an einen abgeleiteten Typ vererbt
werden, sind Konstruktoren. Der Grund dafür liegt in einer Definition der
objektorientierten Programmierung, in der die Aufgabe von Konstruktoren beschrieben
wird. Diese liegt darin, ein vollständig initialisiertes Objekt zurückzugeben.
Da ein abgeleiteter Typ in der Regel weitere Felder einführt, die der Konstruktor
des Basistyps nicht berücksichtigt, würde dieser der Anforderung nicht mehr gerecht,
ein vollständig initialisiertes Objekt zurückzugeben. Eine abgeleitete Klasse
verfügt daher zunächst nur über einen parameterlosen, leeren Standardkonstruktor.
Allerdings können entsprechende Konstruktoren definiert werden. Analog zu Methoden
ist auch in den Konstruktoren der Zugriff auf die Konstrutoren des Basistyps
möglich, wiederum mit Hilfe des Schlüsselwortes
base, das mit der gleichen Syntax
wie das Schlüsselwort
this bei Konstruktoren angegeben werden kann. Wird es
angegeben, wird zunächst der Konstruktor des Basistyps aufgerufen, bevor der
Konstruktor des zu instanziierenden Typs ausgeführt wird. Allerdings kann
nur entweder
base oder
this angegeben werden.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents a base class.
/// </summary>
public class BaseClass
{
/// <summary>
/// Initializes an instance of the BaseClass type.
/// </summary>
public BaseClass()
{
}
}
/// <summary>
/// Represents a class that derives from BaseClass.
/// </summary>
public class DerivedClass : BaseClass
{
/// <summary>
/// Initializes an instance of the DerivedClass
/// type.
/// </summary>
public DerivedClass()
: base()
{
}
}
}
|