C# Objektorientierte Programmierung - Tutorials.de
C# Objektorientierte Programmierung - Tutorials.de
C# Objektorientierte Programmierung - Tutorials.de
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>C#</strong><br />
<strong>Objektorientierte</strong><br />
<strong>Programmierung</strong><br />
1. Vorwort<br />
2. Einleitung<br />
3. Entwicklungsumgebung<br />
4. Grundlagen<br />
5. Debugging<br />
6. Objektorientierung<br />
(Ver. 1.03)<br />
7. Fehlerbehandlung<br />
by Erik Bartmann<br />
1
1 <strong>Objektorientierte</strong> <strong>Programmierung</strong> ..............................................................4<br />
Grundlagen.......................................................................................................4<br />
Klassen und Objekte ........................................................................................5<br />
Klassen<strong>de</strong>finition .............................................................................................5<br />
Instanzvariablen ...............................................................................................7<br />
Objektgenerierung............................................................................................8<br />
Mehrere Objekte generieren.......................................................................12<br />
Unterschie<strong>de</strong> bei <strong>de</strong>r Zuweisung................................................................16<br />
Metho<strong>de</strong>n .......................................................................................................20<br />
Zugriffsmodifizierer...................................................................................25<br />
Wertübergabe.............................................................................................26<br />
Call by value ..............................................................................................31<br />
Call by reference........................................................................................33<br />
Wertrückgabe.............................................................................................34<br />
Wissenswertes über return .........................................................................36<br />
Metho<strong>de</strong>nüberladung .................................................................................38<br />
Beliebig viele Argumente ..........................................................................41<br />
Konstruktoren (spezialisierte Metho<strong>de</strong>n).......................................................44<br />
Konstruktorüberladung ..............................................................................48<br />
Standardkonstruktor...................................................................................49<br />
Konstruktor ruft Konstruktor .....................................................................50<br />
this..................................................................................................................52<br />
Garbage Collection ........................................................................................53<br />
2
Destruktoren ..................................................................................................55<br />
Eigenschaften.................................................................................................58<br />
Konstanten .....................................................................................................62<br />
Vererbung ......................................................................................................63<br />
Grundlagen ................................................................................................63<br />
Ableiten von einer Basisklasse ..................................................................64<br />
Kindklasse ruft Basisklasse .......................................................................69<br />
Konstruktoren im Kontext <strong>de</strong>r Vererbung .................................................71<br />
Welcher Konstruktor wird wann aufgerufen?............................................71<br />
Member ver<strong>de</strong>cken ....................................................................................73<br />
Polymorphie...............................................................................................76<br />
Statische Klassen ...........................................................................................82<br />
Konstruktoren ............................................................................................85<br />
Globale Variablen......................................................................................89<br />
Main-Parameter .........................................................................................92<br />
Enumerationen...............................................................................................96<br />
Elemente <strong>de</strong>r Enumeration ausgeben.......................................................100<br />
Werte <strong>de</strong>r Konstanten ausgeben ..............................................................102<br />
Konstantennamen ausgeben.....................................................................103<br />
Basisdatentyp zur Laufzeit ermitteln .......................................................105<br />
3
1 <strong>Objektorientierte</strong> <strong>Programmierung</strong><br />
Grundlagen<br />
Das folgen<strong>de</strong> Kapitel befasst sich mit <strong>de</strong>r <strong>Objektorientierte</strong>n <strong>Programmierung</strong> -<br />
kurz OOP genannt - unter <strong>C#</strong>. Was sich so leicht dahinsagt, bereitet manchen<br />
am Anfang möglicherweise Kopfschmerzen. Doch lassen Sie sich durch diese<br />
Aussage nicht entmutigen, <strong>de</strong>nn wir wer<strong>de</strong>n auf unterster Ebene beginnen.<br />
Gera<strong>de</strong> für Entwickler, die aus <strong>de</strong>r Ecke <strong>de</strong>r prozeduralen <strong>Programmierung</strong><br />
(z.B. C o<strong>de</strong>r Basic) kommen, ist diese neue Denkweise möglicherweise etwas<br />
ungewohnt. Die Entwickler von objektorientierten Sprachen wie z.B. <strong>C#</strong>, C++,<br />
VB.NET, Java, Delphi, um nur einige zu nennen, waren bestrebt, die Realität so<br />
naturgetreu wie möglich abzubil<strong>de</strong>n. Alles, was wir so um uns herum sehen,<br />
kann angefasst und auf allgemeiner Ebene als Objekt angesehen wer<strong>de</strong>n. Da<br />
haben wir auch schon <strong>de</strong>n Begriff, von <strong>de</strong>r die o.g. Sprachen ihren Zusatz<br />
haben. Betrachten wir in unserem Fall nur Objekte, die von Menschenhand<br />
gefertigt wur<strong>de</strong>n o<strong>de</strong>r noch wer<strong>de</strong>n. Mein folgen<strong>de</strong>s Beispiel liegt zwar etwas in<br />
<strong>de</strong>r Zukunft, doch die ist ja Dank StarTrek in greifbare Nähe gerückt. Die<br />
Konstrukteure eines fiktiven Raumschiffs haben sich natürlich vor <strong>de</strong>m Bau<br />
Gedanken über Aussehen, Größe, Spezifikationen bzw. Bewaffnung gemacht.<br />
Sie haben nicht einfach drauf los gebaut, um zu sehen, (ob) was daraus wird,<br />
son<strong>de</strong>rn einen Konstruktionsplan erstellt, in <strong>de</strong>m alle Details über die spätere<br />
Realisierung <strong>de</strong>s Projektes enthalten sind. Mit dieser Vorlage kann ein einzelnes<br />
o<strong>de</strong>r auch eine ganze Flotte von Raumschiffen geschaffen wer<strong>de</strong>n. Übertragen<br />
wir diese Vorlage auf die <strong>Objektorientierte</strong> <strong>Programmierung</strong>, so sprechen wir<br />
von einer Klasse. Diese Klasse dient als Schablone für zukünftig zu<br />
realisieren<strong>de</strong> Objekte und enthält Anweisungen wie diese aufzubauen sind. Da<br />
haben wir auch schon die bei<strong>de</strong>n ersten wichtigen Schlagwörter <strong>de</strong>r OOP.<br />
Klasse und Objekt. Sie erkennen vielleicht jetzt schon, dass es sich hierbei um<br />
4
eine Vorgehensweise han<strong>de</strong>lt, die versucht ist, die Realität möglichst<br />
naturgetreu abzubil<strong>de</strong>n.<br />
Klassen und Objekte<br />
Eine Klasse ist ein Bauplan bzw. eine Vorlage, die beschreibt, wie ein Objekt<br />
zu realisieren ist. Objekte wer<strong>de</strong>n auch als Klasseninstanzen bezeichnet.<br />
In <strong>de</strong>r OOP nennt man <strong>de</strong>n Übergang von <strong>de</strong>r Klasse zum Objekt<br />
Instanziierung.<br />
Klassen<strong>de</strong>finition<br />
Eine Klassen<strong>de</strong>finition wird immer mit <strong>de</strong>m Schlüsselwort class eingeleitet,<br />
wobei <strong>de</strong>r darauf folgen<strong>de</strong> Klassenname mit einem Großbuchstaben beginnen<br />
sollte. Wie<strong>de</strong>r eine Konvention, an die Sie sich halten sollten, aber nicht<br />
müssen.<br />
5
Folgen<strong>de</strong> drei Bereiche gibt es bei <strong>de</strong>r allgemeinen Klassen<strong>de</strong>finition<br />
1. Vergabe <strong>de</strong>s Klassenname<br />
2. Deklaration <strong>de</strong>r Instanzvariablen<br />
3. Deklaration <strong>de</strong>r Metho<strong>de</strong>n<br />
Wir halten fest, dass ein Objekt die Bün<strong>de</strong>lung von Daten (Instanzvariablen)<br />
und <strong>de</strong>n entsprechen<strong>de</strong>n Funktionen (Metho<strong>de</strong>n) zur Bearbeitung dieser<br />
Daten ist.<br />
6
Instanzvariablen<br />
Schauen wir uns einmal das Raumschiff als Objekt genauer an. Wie können wir<br />
es beschreiben, bzw. welche statischen Attribute, auch Eigenschaften genannt,<br />
weist das Objekts auf?<br />
♦ Länge<br />
♦ Breite<br />
♦ Farbe<br />
♦ Bautyp<br />
♦ Geschwindigkeit<br />
♦ Höchstgeschwindigkeit<br />
♦ Anzahl <strong>de</strong>r Besatzung<br />
♦ Waffen<br />
Das sind nur ein paar Beispiel-Eigenschaften und wir wollen es hierbei<br />
bewen<strong>de</strong>n lassen. Unser Raumschiff hat z.B. eine Länge von 200 Metern, eine<br />
Breite von 100 Metern. Die Farbe ist silber-grau und seine momentane<br />
Geschwindigkeit beträgt Warp 2. Es ist mit einer 200 Mann starken Besatzung<br />
unterwegs. Diese Eigenschaften müssen irgendwie gespeichert wer<strong>de</strong>n und was<br />
liegt näher, als Variablen dafür zu verwen<strong>de</strong>n. Jedoch wer<strong>de</strong>n die Variablen im<br />
Kontext <strong>de</strong>r OOP, Instanzvariablen o<strong>de</strong>r Fel<strong>de</strong>r genannt. Die allgemeine<br />
Syntax zur Deklaration von Instanzvariablen lautet:<br />
Zugriffstyp Datentyp Variablenname;<br />
Bis auf <strong>de</strong>n Zugriffstyp public ähnelt die Deklaration <strong>de</strong>r, einer lokalen<br />
Variablen. Schauen wir uns einmal folgen<strong>de</strong> Klassen<strong>de</strong>finition an:<br />
7
Dotty: „Was be<strong>de</strong>utet <strong>de</strong>nn das Schlüsselwort public?“<br />
Es ist ein Zugriffsmodifizierer, <strong>de</strong>r festlegt, wie auf das entsprechen<strong>de</strong> Element<br />
zugegriffen wer<strong>de</strong>n darf. Die genauere Be<strong>de</strong>utung kommt ein wenig später. Wir<br />
können es an dieser Stelle erst einmal ignorieren.<br />
Objektgenerierung<br />
Dotty: „Nach<strong>de</strong>m wir jetzt eine Klasse <strong>de</strong>finiert haben, stellt sich mir Frage,<br />
was wir mit <strong>de</strong>r Klasse machen und wie wir sie nutzen können?“<br />
Durch eine Klassen<strong>de</strong>finition wird quasi ein neuer Datentyp erzeugt. Die<br />
Deklaration einer Variablen bereitet nach <strong>de</strong>m Studium <strong>de</strong>r Grundlagen<br />
hoffentlich keine Probleme mehr. Sie erfolgt nach <strong>de</strong>m Schema:<br />
Datentyp Variablenname;<br />
Mit <strong>de</strong>r Anweisung<br />
Raumschiff meinSpee<strong>de</strong>r;<br />
wird jedoch in <strong>C#</strong> noch kein konkretes Objekt erzeugt. Es existiert lediglich die<br />
Objektvariable meinSpee<strong>de</strong>r, die später als eine Art Zeiger, auch Referenz<br />
genannt, auf die Startadresse eines bestimmten Speicherbereich (Heap)<br />
verweist. Im Moment besitzt sie jedoch <strong>de</strong>n Wert null und ist lediglich<br />
8
vorbereitet wor<strong>de</strong>n, auf ein Objekt zu zeigen. Wie schon eingangs erwähnt,<br />
existieren im CTS (Common Type System) von .NET zwei unterschiedliche<br />
Kategorien von Datentypen.<br />
♦ Wertetypen<br />
♦ Verweis- bzw. Referenztypen<br />
Wertetypen speichern ihren Wert direkt und wer<strong>de</strong>n im Stack abgelegt, wogegen<br />
Verweisvariablen eine Referenz auf einen Speicherbereich, <strong>de</strong>m Heap, besitzen,<br />
in <strong>de</strong>m die eigentlichen Daten gespeichert sind.<br />
Abb. 1:<br />
new<br />
Wertetypen und Referenztypen<br />
Um ein reales Objekt aus objektorientierter Sicht zu bekommen, wird das<br />
Schlüsselwort new verwen<strong>de</strong>t. Erst danach wird ein Speicherbereich im Heap<br />
bereitgestellt. Die folgen<strong>de</strong> Anweisung generiert das Objekt mit Hilfe <strong>de</strong>r<br />
Klassen<strong>de</strong>finition:<br />
9
meinSpee<strong>de</strong>r = new Raumschiff();<br />
Schauen wir uns das komplette Programm an und versetzten es über einen<br />
Haltepunkt nach <strong>de</strong>m Start mit <strong>de</strong>r F5 - Taste in <strong>de</strong>n Debug-Modus. Stören Sie<br />
sich im folgen<strong>de</strong>n Beispiel nicht an <strong>de</strong>n Schlangenlinien unterhalb <strong>de</strong>r<br />
Instanzvariablen. Sie besagen nur, dass sie im Moment noch keinen Wert<br />
zugewiesen bekommen haben und <strong>de</strong>shalb einen Standartwert besitzen. Welche<br />
das sind, erfahren Sie über das Fenster Lokal nach erreichen <strong>de</strong>s Debug-Modus.<br />
Zeile<br />
Erklärung<br />
10 - 17 Klassen<strong>de</strong>finition <strong>de</strong>r Klasse Raumschiff. In ihr wer<strong>de</strong>n Instanzvariablen <strong>de</strong>klariert, die<br />
10
zur Aufnahme von Objekteigenschaften dienen.<br />
24 Deklaration <strong>de</strong>r Objektvariablen meinSpee<strong>de</strong>r mittels <strong>de</strong>r Klassen<strong>de</strong>finition<br />
Raumschiff. (Es wur<strong>de</strong> noch kein Objekt erzeugt!)<br />
26 Instanziierung <strong>de</strong>s Objektes meinSpee<strong>de</strong>r mit Hilfe <strong>de</strong>s Schlüsselwortes new. Erst jetzt<br />
existiert ein reales Objekt im Heap.<br />
Tab. 1: Co<strong>de</strong>-Erläuterungen<br />
Starten Sie die Anwendung, wird die Ausführung in Zeile 27 über <strong>de</strong>n gesetzten<br />
Haltepunkt unterbrochen. Ein Blick in Fenster Lokal, gibt Aufschluss über die<br />
zu diesem Zeitpunkt initialisierten Variablen.<br />
Abb. 2:<br />
Fenster Lokal im Debug-Modus<br />
Wir erkennen, dass es eine Variable mit Namen meinSpee<strong>de</strong>r vom Datentyp<br />
ConsoleApplication16.Raumschiff gibt. Der Name ConsoleApplication16 gibt<br />
in unserem Fall <strong>de</strong>n Namespace an. Die angezeigten Werte entsprechen <strong>de</strong>n<br />
Defaultzuweisungen, welche implizit vorgenommen wer<strong>de</strong>n.<br />
Icon<br />
Erklärung<br />
Signalisiert, dass es sich um eine Variable han<strong>de</strong>lt<br />
Tab. 2: Iconerklärung<br />
11
Deklaration und Instanziierung können in einer einzigen Anweisungszeile<br />
zusammengefasst wer<strong>de</strong>n.<br />
Zugriff auf Eigenschaften<br />
Da wir über die Klassen<strong>de</strong>finition einen neuen Datentyp geschaffen haben, stellt<br />
sich uns die Frage, wie man nach <strong>de</strong>r Objektgenerierung auf die Eigenschaften<br />
zugreifen kann. Der Punktoperator . wur<strong>de</strong> von uns schon mehrfach verwen<strong>de</strong>t,<br />
um eine Verbindung zwischen Objekt und Member herzustellen. (z.B.<br />
Console.ReadLine()) Der Zugriff erfolgt mit <strong>de</strong>r Syntax:<br />
Objekt.Member<br />
Soll unser Raumschiff meinSpee<strong>de</strong>r mit 200 Mann Besatzung auf die Reise<br />
gehen, dann lautet die Anweisung:<br />
meinSpee<strong>de</strong>r.intBesatzung = 200;<br />
Mehrere Objekte generieren<br />
Der Grund einer Klasse bzw. Vorlage ist natürlich die Wie<strong>de</strong>rverwendbarkeit.<br />
Was nützt mir ein einziges Raumschiff, wenn ich mich gegen einen mächtigen<br />
Gegner behaupten muss? Gar nichts! Aus diesem Grund muss ich mehrere<br />
Kopien anhand <strong>de</strong>r Vorlage anfertigen.<br />
12
Die folgen<strong>de</strong> Anwendung nutzt die gleiche Klassen<strong>de</strong>finition, instanziiert<br />
jedoch 2 zwei Objekte:<br />
13
Die bei<strong>de</strong>n Objekte meinSpee<strong>de</strong>r bzw. <strong>de</strong>inSpee<strong>de</strong>r besitzen <strong>de</strong>n gleichen<br />
Ursprung: die Klassen<strong>de</strong>finition Raumschiff. Sie wer<strong>de</strong>n jeweils in <strong>de</strong>n Zeilen<br />
26 und 27 instanziiert. Es sind aber eigenständige Objekte, die sonst keine<br />
Gemeinsamkeiten besitzen. Je<strong>de</strong>s von Ihnen hat eigene Eigenschaften bzw.<br />
Attribute (Länge, Breite, Farbe und Besatzung). Ein Blick in das Fenster Lokal<br />
gibt Aufschluss über die bei<strong>de</strong>n unabhängigen Objekte.<br />
14
Abb. 3:<br />
Fenster Lokal im Debug-Modus<br />
Hier ein Tipp, <strong>de</strong>r das Arbeiten bzw. erkennen von Objekt-Membern extrem<br />
erleichtert. Wie<strong>de</strong>r leistet IntelliSense unschätzbare Dienste.<br />
Wenn Sie die Eigenschaften eines Objektes initialisieren wollen, geben Sie <strong>de</strong>n<br />
Namen <strong>de</strong>r Objektvariablen, gefolgt vom Punktoperator . ein. Augenblicklich<br />
mel<strong>de</strong>t sich IntelliSense und bietet die Member zur Auswahl an, welche mit<br />
<strong>de</strong>m Objekt verknüpft sind. Sie erkennen aufgrund <strong>de</strong>r optischen<br />
15
Differenzierung (unterschiedliche Icons) sofort die Instanzvariablen wie<strong>de</strong>r. Es<br />
gibt aber eine Bedingung, auf die wir noch zu sprechen kommen: „Es wer<strong>de</strong>n<br />
hier nur Instanzvariablen zur Auswahl angeboten, die <strong>de</strong>n Zugriffsmodifizierer<br />
public haben.“<br />
Unterschie<strong>de</strong> bei <strong>de</strong>r Zuweisung<br />
Kommen wir zu einem gravieren<strong>de</strong>n Unterschied bei <strong>de</strong>r Zuweisung zwischen<br />
Wertetypen und Referenztypen. Das folgen<strong>de</strong> Beispiel zeigt eine<br />
Wertzuweisung, bei <strong>de</strong>m <strong>de</strong>r Wert <strong>de</strong>r Variablen auf <strong>de</strong>r rechten Seite in die<br />
Variable auf <strong>de</strong>r linken Seite kopiert wird. Es existieren zwei unabhängige<br />
Variablen.<br />
Die Konsolen-Ausgabe lautet:<br />
Wert1: 10<br />
Wert2: 42<br />
16
Zeile<br />
Erklärung<br />
14 Die Variable intWert1 wird <strong>de</strong>klariert und mit <strong>de</strong>m Wert 10 initialisiert<br />
15 Die Variable intWert2 wird <strong>de</strong>klariert und mit einer Kopie <strong>de</strong>s Wertes von<br />
intWert1 initialisiert.<br />
16 Der Variablen intWert2 wird <strong>de</strong>r Wert 42 zugewiesen, was aber keine<br />
Auswirkung auf <strong>de</strong>n Inhalt von intWert1 hat.<br />
Tab. 3: Co<strong>de</strong>-Erläuterungen<br />
Das folgen<strong>de</strong> Beispiel zeigt Ihnen die Zuweisung bei Referenztypen. Sie erfolgt<br />
in einer Weise, die Sie vielleicht nicht vermutet haben.<br />
17
Die Konsolen-Ausgabe lautet:<br />
99<br />
18
Bis zur Zeile 39 besitzen wir zwei eigenständige Raumschiffe (meinSpee<strong>de</strong>r,<br />
<strong>de</strong>inSpee<strong>de</strong>r), die über je eine Objektreferenz anzusprechen sind. Je<strong>de</strong>s von<br />
ihnen besitzt individuelle Eigenschaften, was <strong>de</strong>r Zustand „vorher“<br />
wi<strong>de</strong>rspiegelt. In Zeile 42 erfolgt nun durch meinSpee<strong>de</strong>r =<br />
<strong>de</strong>inSpee<strong>de</strong>r nicht etwa ein Kopieren <strong>de</strong>s Objektes <strong>de</strong>inSpee<strong>de</strong>r nach<br />
meinSpee<strong>de</strong>r, son<strong>de</strong>rn ein Kopieren <strong>de</strong>r Referenz. Es existieren ab dieser Zeile<br />
zwei Objektreferenzen, die auf ein und dasselbe Objekt verweisen, was <strong>de</strong>r<br />
Zustand „nachher“ wi<strong>de</strong>rspiegelt. Sollten Sie jetzt versucht sein, über die<br />
Objektreferenz meinSpee<strong>de</strong>r das Objekt zu manipulieren (Zeile 43), kann<br />
natürlich über die an<strong>de</strong>re Referenz <strong>de</strong>inSpee<strong>de</strong>r das Ergebnis abgefragt wer<strong>de</strong>n<br />
(Zeile 44). Egal welchen Zugang zum Objekt Sie wählen, Sie sprechen nur ein<br />
Objekt an.<br />
Dotty: „Wie kommen wir jetzt wie<strong>de</strong>r an das alte Raumschiff meinSpee<strong>de</strong>r<br />
heran?“<br />
19
Das ist das Problem. Gar nicht mehr. Verliert ein Objekt seine letzte Referenz,<br />
ist es nicht mehr ansprechbar und wird durch eine Einrichtung, die Garbage<br />
Collection genannt wird, entsorgt. Es ist die interne .NET Müllabfuhr, die alle<br />
herrenlosen Objekte aufspürt und <strong>de</strong>n zuvor belegten Speicherplatz wie<strong>de</strong>r<br />
freigibt. Das hat natürlich enorme Vorteile, <strong>de</strong>nn Sie brauchen sich nicht weiter<br />
um diese Angelegenheit wie z.B. in <strong>de</strong>r Programmiersprache C++ zu kümmern.<br />
Wird dort <strong>de</strong>r Aufräumprozess vergessen, kommt es zu so genannten<br />
Speicherlecks, die unter Umstän<strong>de</strong>n zur Laufzeit eines Programms <strong>de</strong>n<br />
kostbaren freien Speicher nach und nach aufbrauchen. Weitere Details erfahren<br />
Sie im Kapitel über die Garbage Collection.<br />
Metho<strong>de</strong>n<br />
Wie Sie in <strong>de</strong>r Klassen<strong>de</strong>finition gesehen haben, gibt es neben <strong>de</strong>n<br />
Instanzvariablen noch einen weiteren elementaren Bestandteil einer Klasse. Es<br />
sind die Metho<strong>de</strong>n. Sie sind Unterprogramme, die eine o<strong>de</strong>r mehrere<br />
Anweisungen enthalten können. Von außen betrachtet kann sie als eine einzelne<br />
Anweisung angesehen wer<strong>de</strong>n, die über ihren Metho<strong>de</strong>nnamen aufgerufen wird.<br />
Eine äußerst wichtige haben Sie schon im Kapitel über die Grundlagen kennen<br />
gelernt. Es ist die Metho<strong>de</strong> Main. Die allgemeine Syntax lautet:<br />
Zugriffstyp Rückgabetyp Metho<strong>de</strong>nname(Parameterliste)<br />
{<br />
// Metho<strong>de</strong>nrumpf<br />
// Anweisung(en)<br />
}<br />
Was be<strong>de</strong>uten die einzelnen Elemente bei <strong>de</strong>r<br />
Definition einer Metho<strong>de</strong>?<br />
Element<br />
Zugriffstyp<br />
Erklärung<br />
Der Zugriff wird über einen so genannten Zugriffsmodifizierer gesteuert<br />
20
und legt fest, wer die Metho<strong>de</strong> im aktuellen Kontext aufrufen darf.<br />
Nähere Informationen liefert <strong>de</strong>r Abschnitt über Zugriffsmodifizierer.<br />
Rückgabetyp Der Rückgabetyp legt fest, welchen Datentyp die Metho<strong>de</strong><br />
zurückliefert. Wird kein Wert zurückgeliefert, muss <strong>de</strong>r Typ auf void<br />
gesetzt wer<strong>de</strong>n.<br />
Metho<strong>de</strong>nname<br />
Parameterliste<br />
Der Name, mit <strong>de</strong>m die Metho<strong>de</strong> aufgerufen wird. Er kann frei gewählt<br />
wer<strong>de</strong>n, darf jedoch kein Schlüsselwort sein.<br />
Die Parameterliste befin<strong>de</strong>t sich innerhalb <strong>de</strong>s run<strong>de</strong>n Klammernpaares<br />
(...) nach <strong>de</strong>m Metho<strong>de</strong>nnamen und beinhaltet die<br />
Datentypbezeichnungen und Variablennamen, die zur Aufnahme <strong>de</strong>r<br />
übergebenen Argumente dienen. Mehrere Parameter wer<strong>de</strong>n durch<br />
Kommata voneinan<strong>de</strong>r getrennt angegeben. Wer<strong>de</strong>n keine Argumente<br />
übergeben, ist die Parameterliste leer, wobei die run<strong>de</strong>n Klammern<br />
nicht entfallen dürfen.<br />
Metho<strong>de</strong>nrumpf Der Metho<strong>de</strong>nrumpf wird durch das geschweifte Klammernpaar {...}<br />
festgelegt und enthält die Anweisung(en), die die Funktionsweise <strong>de</strong>r<br />
Metho<strong>de</strong> ausmachen.<br />
Tab. 4: Metho<strong>de</strong>n-Elemente<br />
Mit Hilfe <strong>de</strong>r Metho<strong>de</strong>n kann auf die Daten einer Klasse zugegriffen wer<strong>de</strong>n,<br />
um sie zu lesen o<strong>de</strong>r auch zu modifizieren.<br />
Dotty: „Stop, stop, dass ist meines Wissens nicht ganz korrekt. Der Zugriff<br />
auf Eigenschaften erfolgte im letzten Beispiel ohne eine Metho<strong>de</strong>. Es wur<strong>de</strong><br />
einfach die Objektvariable und <strong>de</strong>ssen Eigenschaft angegeben.“<br />
Dem muss ich zustimmen, doch das ist nicht die Regel und diente nur als<br />
anfängliches Beispiel. Ich komme jetzt zu einem weiteren wichtigen Eckpfeiler<br />
<strong>de</strong>r OOP. Es ist die Kapselung. Der Zugriff auf die Eigenschaften eines<br />
Objekts, die in Instanzvariablen abgelegt wer<strong>de</strong>n, erfolgte bisher direkt. Durch<br />
die Anweisung<br />
21
meinSpee<strong>de</strong>r.intBesatzung = 200;<br />
war es möglich, die Eigenschaft <strong>de</strong>s Objektes unmittelbar zu manipulieren. Die<br />
folgen<strong>de</strong> Grafik veranschaulicht <strong>de</strong>n Sachverhalt.<br />
Abb. 4:<br />
Direkter Zugriff auf die Daten eines Objekts<br />
Das war jedoch nur möglich, weil <strong>de</strong>r Zugriffsmodifizierer <strong>de</strong>r betreffen<strong>de</strong>n<br />
Eigenschaft auf public (öffentlich) steht. Ein Zugriff von außen auf die Daten<br />
<strong>de</strong>s Objekts ist somit erlaubt. Stellen Sie sich vor, Sie sind Captain dieses<br />
Raumschiffs und stellen auf einmal fest, dass sich an Bord mehr<br />
Besatzungsmitglie<strong>de</strong>r befin<strong>de</strong>n, als es zur Erfüllung Ihres Auftrages notwendig<br />
ist. Aufgrund <strong>de</strong>s begrenzten Platzangebotes kommt es nach kurzer Zeit zu<br />
Streitigkeiten untereinan<strong>de</strong>r, was die gesamte Mission gefähr<strong>de</strong>t.<br />
22
Was ist genau passiert? Durch die Anweisung<br />
meinSpee<strong>de</strong>r.intBesatzung = 750;<br />
ist die maximal zulässige Besatzungsstärke von 200 Mann um mehr als das 3-<br />
fache überschritten wor<strong>de</strong>n. Niemand hat sich verantwortlich gefühlt eine<br />
Zugangskontrolle einzurichten, um ein <strong>de</strong>rartiges Problem erst gar nicht<br />
entstehen zu lassen.<br />
Dotty: „Was können wir aber unternehmen, damit es nicht dazu kommt?“<br />
Wir müssen zwei Modifikationen vornehmen:<br />
1. Den öffentlichen Zugriff von außen unterbin<strong>de</strong>n<br />
2. Den Zugang über eine Kontrollinstanz überwachen lassen, so dass<br />
eingeschritten wer<strong>de</strong>n kann, wenn die maximale o<strong>de</strong>r auch minimale<br />
Besatzungsanzahl über- bzw. unterschritten wird.<br />
Das be<strong>de</strong>utet im Detail, dass in Schritt 1 in <strong>de</strong>r Deklarationszeile <strong>de</strong>r<br />
Instanzvariablen <strong>de</strong>r Zugriffsmodifizierer public (öffentlich) durch private<br />
(privat) ersetzt wer<strong>de</strong>n muss. Durch diese Modifikation kann auf die<br />
Instanzvariable nur noch innerhalb <strong>de</strong>r Klasse zugegriffen wer<strong>de</strong>n. Da die<br />
Vorgehensweise für alle bis jetzt genannten Instanzvariablen gleich ist,<br />
konzentrieren wir uns im nun folgen<strong>de</strong>n Beispiel auf intBesatzung.<br />
23
private int intBesatzung;<br />
Versuchen wir jetzt wie bisher auf die Eigenschaft durch<br />
meinSpee<strong>de</strong>r.intBesatzung = 750;<br />
zuzugreifen, erhalten wir eine Fehlermeldung:<br />
Das ist genau das, was beabsichtigt war. Jetzt kommt Schritt 2. Der Zugriff auf<br />
die Daten ist nur über eine öffentliche Metho<strong>de</strong> gestattet:<br />
Abb. 5:<br />
Indirekter Zugriff auf die Daten eines Objektes<br />
Sie sehen, dass zwischen <strong>de</strong>m (öffentlichen) Zugriff und <strong>de</strong>n (privaten) Daten<br />
ein Element eingefügt wur<strong>de</strong>. Die Metho<strong>de</strong>. Sie übernimmt bei entsprechen<strong>de</strong>r<br />
Kodierung die Überwachung <strong>de</strong>s Zugangs zum Raumschiff und stellt das<br />
Bin<strong>de</strong>glied zwischen <strong>de</strong>n privaten, gekapselten Instanzvariablen und <strong>de</strong>r<br />
Außenwelt dar. Das ist genau die Strategie, die die OOP verfolgt. Die<br />
24
Manipulation <strong>de</strong>r von außen unsichtbaren Eigenschaften ist nur über öffentliche<br />
Metho<strong>de</strong>n zulässig bzw. gewünscht.<br />
Da wir an <strong>de</strong>m Punkt gelangt sind, wo Sie über Eigenschaften und Metho<strong>de</strong>n<br />
bescheid wissen, erkennen Sie, dass in <strong>de</strong>r OOP die Daten (Instanzvariablen)<br />
und die Funktionen (Metho<strong>de</strong>n) zur Manipulation <strong>de</strong>rselben, eine Einheit<br />
(Objekt) bil<strong>de</strong>n.<br />
Zugriffsmodifizierer<br />
Jetzt wird es Zeit, ein paar Worte über die schon mehrfach verwen<strong>de</strong>ten<br />
Zugriffsmodifizierer public bzw. private zu verlieren. Sie legen die Sichtbarkeit<br />
bzw. Zugriffsmöglichkeit <strong>de</strong>r Klassenelemente, auch Member genannt, fest.<br />
Zugriffsmodifizierer<br />
public<br />
private<br />
protected<br />
Be<strong>de</strong>utung<br />
Auf Klassenelemente, die als public <strong>de</strong>klariert wur<strong>de</strong>n, kann über<br />
eine Referenz auf die Klasse zugegriffen wer<strong>de</strong>n.<br />
Auf Klassenelemente, die als private <strong>de</strong>klariert wur<strong>de</strong>n, kann nur von<br />
innerhalb <strong>de</strong>r Klasse zugegriffen wer<strong>de</strong>n. In abgeleiteten Klassen<br />
kann auf diese Elemente nicht zugegriffen wer<strong>de</strong>n.<br />
Gleiches Verhalten, wie mit private. Jedoch kann in abgeleiteten<br />
Klassen auf diese Elemente zugegriffen wer<strong>de</strong>n.<br />
25
Tab. 5: Zugriffsmodifizierer für Klassenelemente<br />
Wird kein Zugriffsmodifizierer angegeben, wird er implizit auf private<br />
gesetzt.<br />
Wertübergabe<br />
Schauen Sie sich die folgen<strong>de</strong> Metho<strong>de</strong> (grauer Kasten) an, die innerhalb <strong>de</strong>r<br />
Klasse Raumschiff <strong>de</strong>finiert wur<strong>de</strong>:<br />
26
Was erkennen wir? Der Metho<strong>de</strong>nkopf in Zeile 16 beginnt mit <strong>de</strong>m<br />
Zugriffsmodifizierer public, <strong>de</strong>r <strong>de</strong>n öffentlichen Zugriff auf die Metho<strong>de</strong><br />
erlaubt. An zweiter Position steht <strong>de</strong>r Rückgabetyp void, <strong>de</strong>r besagt, dass kein<br />
Rückgabewert an <strong>de</strong>n Aufrufer geliefert wird. Als nächstes kommt <strong>de</strong>r<br />
Metho<strong>de</strong>nname mit <strong>de</strong>r Parameterliste in <strong>de</strong>n run<strong>de</strong>n Klammern. Dort fin<strong>de</strong>t<br />
27
sich nur ein Parameter (Variable) <strong>de</strong>s Datentyps int. Rufen wir die Metho<strong>de</strong> z.B.<br />
mit <strong>de</strong>r folgen<strong>de</strong>n Anweisung<br />
meinSpee<strong>de</strong>r.setBesatzung(150);<br />
auf, wird <strong>de</strong>r Wert 150 als Argument <strong>de</strong>m Parameter intAnzahl übergeben.<br />
Die if-Anweisung überprüft, ob sich <strong>de</strong>r Wert innerhalb <strong>de</strong>r Grenzen von 20 bis<br />
200 befin<strong>de</strong>t. Liegt er außerhalb, wird die Instanzvariable intBesatzung auf<br />
<strong>de</strong>n Wert 0 gesetzt und eine entsprechen<strong>de</strong> Fehlermeldung auf <strong>de</strong>r Konsole<br />
ausgegeben. Bei Einhaltung <strong>de</strong>r gesetzten Grenzen wird <strong>de</strong>r übergebene Wert<br />
<strong>de</strong>r Variablen intAnzahl an die Instanzvariable intBesatzung übergeben<br />
(Zeile 27) und eine Bestätigung an die Konsole geschickt. Auf diese Weise<br />
kann es nie zu einer Überladung unseres Raumschiffs bezüglich <strong>de</strong>r<br />
Besatzungsanzahl kommen. Nach <strong>de</strong>r Eingabe <strong>de</strong>s Objektnamen mit<br />
nachfolgen<strong>de</strong>m Punktoperator zeigt Ihnen übrigens IntelliSense wie<strong>de</strong>r die Liste<br />
<strong>de</strong>r möglichen Member an:<br />
Kapselung<br />
Sie sehen, dass die Metho<strong>de</strong> in ihrer Funktion als Kontrollinstanz arbeitet und<br />
somit die Objektintegrität sicherstellt. In <strong>de</strong>r OOP wird dieses Prinzip<br />
Kapselung genannt. Vielleicht ist Ihnen aufgefallen, dass durch das Än<strong>de</strong>rn <strong>de</strong>s<br />
Zugriffsmodifizierers von public auf private, die Instanzvariable<br />
intBesatzung nicht mehr in <strong>de</strong>r Auswahlliste erscheint. Hier zur Erinnerung<br />
noch einmal die Be<strong>de</strong>utung <strong>de</strong>r Icons:<br />
28
Icon<br />
Erklärung<br />
Signalisiert, dass es sich um eine Metho<strong>de</strong> han<strong>de</strong>lt<br />
Tab. 6: Iconerklärungen<br />
Wenn Sie eine Klasse erstellen, seien Sie bestrebt, die darin enthaltene<br />
Komplexität zu verbergen, zu kapseln. Jemand, <strong>de</strong>r die Klasse nutzt, benötigt<br />
keine Informationen über die internen Vorgänge. Der Zugang sollte über<br />
öffentliche Metho<strong>de</strong>n o<strong>de</strong>r die noch zu besprechen<strong>de</strong>n Eigenschaften erfolgen.<br />
Vielleicht haben Sie schon einmal <strong>de</strong>n Begriff Black-Box gehört. Es ist ein<br />
Gebil<strong>de</strong>, <strong>de</strong>ssen innerstes im Verbogenen liegt und wir wissen nur, dass es eine<br />
bestimmte Aufgabe zu erfüllen hat. Wie es sie erledigt, ist nicht bekannt und<br />
wir müssen es nicht unbedingt wissen. Die Hauptsache ist, dass die Black-Box<br />
funktioniert.<br />
Argument und Parameter<br />
Dotty: „So ganz habe ich <strong>de</strong>n Unterschied zwischen Argumenten und<br />
Parametern noch nicht verstan<strong>de</strong>n!“<br />
Beim Aufruf einer Metho<strong>de</strong>, können ihr Werte übergeben wer<strong>de</strong>n, mit <strong>de</strong>nen sie<br />
arbeitet. Diese bezeichnet man als Argumente. Die Metho<strong>de</strong> ihrerseits muss<br />
diese Argumente entgegen nehmen und in entsprechen<strong>de</strong>n Variablen speichern.<br />
Sie wer<strong>de</strong>n Parameter genannt.<br />
Argument<br />
150<br />
Parameter<br />
public void setBesatzung(int Anzahl)<br />
{<br />
...<br />
}<br />
Abb. 6:<br />
Argument und Parameter<br />
29
Mehrere Parameter<br />
Natürlich können wir die Parameterliste beliebig erweitern. Dazu wer<strong>de</strong>n die<br />
einzelnen Parameter durch Kommata von einan<strong>de</strong>r getrennt angegeben.<br />
Schauen Sie sich die Dimension <strong>de</strong>s Raumschiffs an, die ich <strong>de</strong>r Einfachheit<br />
halber auf Länge und Breite reduziert habe. Es mach wenig Sinn, je eine<br />
Metho<strong>de</strong> für die Länge bzw. für die Breite zu <strong>de</strong>finieren.<br />
30
Zeile<br />
Erklärung<br />
18 - 22 Definition <strong>de</strong>r Metho<strong>de</strong> setDimension(double, double). Beachten<br />
Sie bitte, dass für je<strong>de</strong>n Parameter, obwohl bei<strong>de</strong> <strong>de</strong>n gleichen Datentyp double<br />
besitzen, die explizite Angabe erfor<strong>de</strong>rlich ist. setDimension(double<br />
dblL, dblB) ist nicht zulässig!<br />
32 Aufruf <strong>de</strong>r Metho<strong>de</strong> setDimension(). Beachten Sie die Reihenfolge <strong>de</strong>r<br />
Argumente: Argument1 (300.5) wird an Parameter1 (dblL) bzw. Argument2 (76.7)<br />
an Parameter2 (dblB) übergeben.<br />
Tab. 7: Co<strong>de</strong>-Erläuterungen<br />
Call by value<br />
Beim Aufruf einer Metho<strong>de</strong> erfolgte die Übergabe eines o<strong>de</strong>r mehrerer<br />
Argumente bisher nach <strong>de</strong>m Prinzip <strong>de</strong>r Wertübergabe, auch call by value<br />
genannt. Dabei wer<strong>de</strong>n die Argumente <strong>de</strong>s Aufrufers in die entsprechen<strong>de</strong>n<br />
Parameter <strong>de</strong>r Metho<strong>de</strong> kopiert. Das folgen<strong>de</strong> Beispiel zeigt <strong>de</strong>n Aufruf <strong>de</strong>r<br />
Metho<strong>de</strong> swap, an die zwei Werte übergeben wer<strong>de</strong>n. Sie hat die Aufgabe, die<br />
bei<strong>de</strong>n Werte zu vertauschen. Jedoch fin<strong>de</strong>t <strong>de</strong>r Tausch nur innerhalb <strong>de</strong>r<br />
Metho<strong>de</strong> swap statt, und bezieht sich nur auf die lokalen Variablen intW1 und<br />
intW2. Es fin<strong>de</strong>t keine Rückkopplung an die bei<strong>de</strong>n Variablen intWert1 und<br />
intWert2 statt, die als Argumente <strong>de</strong>r Metho<strong>de</strong> swap eingesetzt wur<strong>de</strong>n.<br />
31
Deshalb lautet die Konsolen-Ausgabe:<br />
Wert1: 10<br />
Wert2: 20<br />
Wir sehen, dass die bei<strong>de</strong>n Werte nicht vertauscht wur<strong>de</strong>n. Wie schaffen wir es<br />
aber trotz<strong>de</strong>m, dass die Metho<strong>de</strong> swap die Aufgabe erfüllt?<br />
32
Call by reference<br />
Fügen wir <strong>de</strong>n Parametern <strong>de</strong>r Metho<strong>de</strong> swap jedoch das Schlüsselwort ref<br />
hinzu, än<strong>de</strong>rt sich das Verhalten <strong>de</strong>r Metho<strong>de</strong>.<br />
Zusätzlich muss <strong>de</strong>r Metho<strong>de</strong>naufruf dahingehend angepasst wer<strong>de</strong>n, dass dort<br />
ebenfalls das Schlüsselwort ref verwen<strong>de</strong>t wird.<br />
Die Konsolen-Ausgabe lautet jetzt:<br />
Wert1: 20<br />
Wert2: 10<br />
Doch wie kommt es zu dieser Wandlung? In diesem Fall wird nicht mit <strong>de</strong>n<br />
Kopien <strong>de</strong>r übergebenen Werte gearbeitet. Der Metho<strong>de</strong>naufruf übergibt die<br />
Speicheradressen (Referenz- o<strong>de</strong>r Verweisübergabe) <strong>de</strong>r Argumente an die<br />
Parameter, was be<strong>de</strong>utet, dass die Parameter als Aliasnamen angesehen wer<strong>de</strong>n<br />
können. Folgen<strong>de</strong> Punkte sind bei call by reference zu beachten:<br />
♦ Die Variablen in <strong>de</strong>r Parameterliste wer<strong>de</strong>n mit <strong>de</strong>m Schlüsselwort ref<br />
gekennzeichnet<br />
♦ Den Argumenten im Metho<strong>de</strong>naufruf muss ebenfalls das Schlüsselwort ref<br />
vorangestellt wer<strong>de</strong>n<br />
33
♦ Der o<strong>de</strong>r die Argumente müssen initialisiert sein<br />
♦ Die Übergabe einer Konstanten ist unzulässig. An<strong>de</strong>rnfalls erhalten Sie die<br />
Fehlermeldung :<br />
Der Vollständigkeit halber, möchte ich noch <strong>de</strong>n Parameter out ansprechen. Er<br />
arbeitet ähnlich, wie ref. Der Unterschied ist jedoch, dass beim Metho<strong>de</strong>naufruf<br />
an <strong>de</strong>n mit out <strong>de</strong>klariertem Parameter keine initialisierte Variable übergeben<br />
wer<strong>de</strong>n muss. Es wer<strong>de</strong>n also keine Daten an die Metho<strong>de</strong> übergeben, son<strong>de</strong>rn<br />
sie steht in <strong>de</strong>r Pflicht, diese zurück zu liefern.<br />
Wertrückgabe<br />
Sie haben gesehen, wie über eine öffentliche Metho<strong>de</strong> eine private<br />
Instanzvariable modifizieren wer<strong>de</strong>n kann. Natürlich ist <strong>de</strong>r umgekehrte Weg,<br />
das Abfragen einer Instanzvariablen, ebenso möglich. Fügen wir <strong>de</strong>r Klasse<br />
Raumschiff das folgen<strong>de</strong> Co<strong>de</strong>fragment hinzu, wird <strong>de</strong>r Wert von<br />
intBesatzung abgefragt.<br />
Was erkennen wir? Der Metho<strong>de</strong>nkopf in Zeile 33 beginnt mit <strong>de</strong>m<br />
Zugriffsmodifizierer public, <strong>de</strong>r <strong>de</strong>n öffentlichen Zugriff auf die Metho<strong>de</strong><br />
erlaubt. An zweiter Position steht <strong>de</strong>r Rückgabetyp int, <strong>de</strong>n die Metho<strong>de</strong><br />
zurückliefert. Als nächstes folgt <strong>de</strong>r Metho<strong>de</strong>nname, <strong>de</strong>ssen Parameterliste<br />
34
jedoch leer ist, da keine Wertübergabe erfolgen soll. In Zeile 35 benutzen wir<br />
das Schlüsselwort return. Es dient dazu, einen bestimmten Wert an <strong>de</strong>n<br />
Aufrufer zurück zuliefern. Die Syntax lautet:<br />
return Variable;<br />
o<strong>de</strong>r<br />
return Wert;<br />
Die folgen<strong>de</strong> Grafik veranschaulicht <strong>de</strong>n schreiben<strong>de</strong>n bzw. lesen<strong>de</strong>n Prozess<br />
auf das Objekt:<br />
35
Die Anweisung zum Abfragen <strong>de</strong>r Instanzvariablen lautet:<br />
Console.WriteLine(meinSpee<strong>de</strong>r.getBesatzung());<br />
Wissenswertes über return<br />
Unerreichbarer Co<strong>de</strong><br />
Vielleicht ist Ihnen in <strong>de</strong>n Sinn gekommen, dass Sie nach <strong>de</strong>m Zurückliefern<br />
<strong>de</strong>s Wertes von intBesatzung noch zusätzlich eine Information an die<br />
Konsole schicken sollten.<br />
Schon rein optisch gesehen (Schlangenlinien) muss am vorliegen<strong>de</strong>n Co<strong>de</strong><br />
etwas nicht stimmen. Zeile 35 liefert <strong>de</strong>n Wert von intBesatzung an <strong>de</strong>n<br />
Aufrufer zurück und verlässt damit augenblicklich die Metho<strong>de</strong>. Das be<strong>de</strong>utet<br />
natürlich, dass die Anweisung in Zeile 36 nie zur Ausführung gebracht wird.<br />
Fahren Sie mit <strong>de</strong>r Maus über die Schlangenlinien, lautet <strong>de</strong>r Hinweis:<br />
„Unerreichbarer Co<strong>de</strong> wur<strong>de</strong> ent<strong>de</strong>ckt.“<br />
return zwingend notwendig<br />
Wur<strong>de</strong> ein Rückgabetyp außer void vereinbart, so ist es zwingend notwendig,<br />
dass die Metho<strong>de</strong> mit return einen Wert zurückliefert. Fin<strong>de</strong>t innerhalb <strong>de</strong>r<br />
Metho<strong>de</strong> z.B. eine Verzweigung über die if-else-Anweisung statt, muss<br />
sichergestellt wer<strong>de</strong>n, dass je<strong>de</strong>r mögliche Ausführungszweig eine return-<br />
Anweisung bereitstellt.<br />
36
Innerhalb <strong>de</strong>r Metho<strong>de</strong> befin<strong>de</strong>t eine Kontrollstruktur, die je nach<br />
Wahrheitsgehalt einen von bei<strong>de</strong>n Co<strong>de</strong>pfa<strong>de</strong>n ausführt. Da jedoch nur <strong>de</strong>r if-<br />
Zweig die Bedingung, einen Rückgabewert zu liefern, erfüllt (Zeile 36), wür<strong>de</strong><br />
es beim Eintritt in <strong>de</strong>n else-Zweig ein Problem geben. Deshalb erhalten wir die<br />
folgen<strong>de</strong> Fehlermeldung:<br />
Sorgen Sie dafür, dass alle möglichen Ausführungspfa<strong>de</strong> in einem solchen Fall<br />
einen Rückgabewert an <strong>de</strong>n Aufrufer liefern.<br />
return auch bei void<br />
Soll eine Metho<strong>de</strong> keinen Wert an <strong>de</strong>n Aufrufer zurück liefern, setzt man <strong>de</strong>n<br />
Rückgabetyp auf void, was übersetzt leer be<strong>de</strong>utet. Eine return-Anweisung ist<br />
dadurch nicht notwendig bzw. optional. Dennoch kann die Metho<strong>de</strong> mit return,<br />
jedoch ohne Angabe eines Rückgabewertes, vorzeitig verlassen wer<strong>de</strong>n.<br />
Zusammenfassend be<strong>de</strong>utet dies, dass eine Metho<strong>de</strong>, die void als<br />
Rückgabetype hat, entwe<strong>de</strong>r durch das Erreichen <strong>de</strong>r schließen<strong>de</strong>n<br />
geschweiften Klammer o<strong>de</strong>r durch eine return-Anweisung verlassen wer<strong>de</strong>n<br />
kann.<br />
37
Metho<strong>de</strong>nüberladung<br />
In <strong>C#</strong> können Metho<strong>de</strong>n überla<strong>de</strong>n wer<strong>de</strong>n, was be<strong>de</strong>utet, dass mehrere mit<br />
gleichem Namen existieren.<br />
Dotty: „Können wir dann immer noch von Ein<strong>de</strong>utigkeit sprechen?“<br />
Es existiert natürlich ein Unterscheidungsmerkmal. Wenn <strong>de</strong>r Name nicht zur<br />
Differenzierung herangezogen wer<strong>de</strong>n kann, konzentrieren wir uns auf die<br />
Parameterliste. Sie muss sich in <strong>de</strong>n einzelnen gleichnamigen Metho<strong>de</strong>n<br />
unterschei<strong>de</strong>n. Dabei dürfen sich die Metho<strong>de</strong>n nicht nur im möglicherweise<br />
vorhan<strong>de</strong>nen Rückgabetyp unterschei<strong>de</strong>n.<br />
38
Die Konsolen-Ausgabe lautet:<br />
Parameterlos<br />
Ein int-Parameter<br />
Zwei int-Parameter<br />
Ein int- / ein double-Parameter<br />
Zeile<br />
Erklärung<br />
38 Aufruf <strong>de</strong>r Metho<strong>de</strong> ohne Parameter. Die Metho<strong>de</strong>n<strong>de</strong>finition in Zeile 13 fühlt sich<br />
39
aufgrund <strong>de</strong>s Fehlens jeglicher Argumente angesprochen.<br />
39 Aufruf <strong>de</strong>r Metho<strong>de</strong> mit einem Ganzzahl-Literal, welches als Datentyp int interpretiert<br />
wird. Die Metho<strong>de</strong>n<strong>de</strong>finition in Zeile 18 wird angesprungen, da sich in <strong>de</strong>ren<br />
Parameterliste eine Variable vom Datentyp int befin<strong>de</strong>t.<br />
40 Aufruf <strong>de</strong>r Metho<strong>de</strong> mit zwei Ganzzahl-Literalen. Die Metho<strong>de</strong>n<strong>de</strong>finition in Zeile 23<br />
fühlt sich angesprochen. Sie besitzt in ihrer Parameterliste zwei Variablen <strong>de</strong>s<br />
Datentyps int.<br />
41 Aufruf <strong>de</strong>r Metho<strong>de</strong> mit einem int bzw. double Wert. Die Metho<strong>de</strong>n<strong>de</strong>finition in Zeile<br />
28 wird angesprungen. Die Parameterliste enthält sowohl eine int-, als auch eine<br />
double-Variable.<br />
Tab. 8: Co<strong>de</strong>-Erläuterungen<br />
Dotty: „Kann man beim Aufruf einer Metho<strong>de</strong> erkennen, ob und wie oft sie<br />
überla<strong>de</strong>n ist?“<br />
Das ist möglich. Vielleicht ist es Ihnen schon beim Tippen <strong>de</strong>r Metho<strong>de</strong><br />
Console.WriteLine() aufgefallen. Nach <strong>de</strong>r Eingabe <strong>de</strong>s Namens, gefolgt von<br />
<strong>de</strong>r run<strong>de</strong>n öffnen<strong>de</strong>n Klammer, zeigt uns IntelliSense die folgen<strong>de</strong>n<br />
Informationen:<br />
Es existieren vier Metho<strong>de</strong>nüberladungen (1 von 4) wovon die erste<br />
parameterlose Version - zu erkennen am leeren run<strong>de</strong>n Klammernpaar - zur<br />
Auswahl angeboten wird. Sie haben jetzt die Möglichkeit mit <strong>de</strong>n Tasten<br />
Cursoroben bzw. Cursounten sich die einzelnen überla<strong>de</strong>nen Metho<strong>de</strong>n mit <strong>de</strong>ren<br />
Parameterliste anzuschauen:<br />
40
Dotty: „Ich verstehe <strong>de</strong>n Sinn <strong>de</strong>s Überla<strong>de</strong>ns noch nicht so ganz.“<br />
Kommen wir noch einmal zu <strong>de</strong>r schon angesprochenen Metho<strong>de</strong><br />
Console.WriteLine(...), die 19-fach überla<strong>de</strong>n ist. Sie ist so flexibel, dass sie als<br />
Argument die unterschiedlichsten Datentypen akzeptiert und über einen<br />
einzigen Namen aufzurufen ist. Wür<strong>de</strong>n die Möglichkeit <strong>de</strong>s Überla<strong>de</strong>ns in <strong>C#</strong><br />
nicht existieren, so müssten wir für je<strong>de</strong>n existieren<strong>de</strong>n Datentyp jeweils eine<br />
eigene Metho<strong>de</strong> schreiben, was zur Folge hätte, dass es unzählige Metho<strong>de</strong>n mit<br />
unterschiedlichen Namen geben wür<strong>de</strong>:<br />
♦ WriteLineByte(...)<br />
♦ WriteLineShort(...)<br />
♦ WriteLineInt(...)<br />
♦ usw.<br />
Könnten Sie sich alle möglichen Metho<strong>de</strong>n alleine für WriteLine merken? Ist<br />
natürlich machbar, doch recht ineffektiv. Durch die Metho<strong>de</strong>nüberladung wird<br />
die Sache enorm vereinfacht und durch die Reduzierung unterschiedlicher<br />
Metho<strong>de</strong>nnamen vor allen Dingen übersichtlicher. Wir übergeben beim Aufruf<br />
einen Wert, soll sich jemand an<strong>de</strong>res darum kümmern, die dazu passen<strong>de</strong><br />
Metho<strong>de</strong> aufzurufen.<br />
Beliebig viele Argumente<br />
Nach<strong>de</strong>m wir jetzt die Funktionsweise <strong>de</strong>r Metho<strong>de</strong>nüberladung kennen gelernt<br />
haben, sehen wir uns das folgen<strong>de</strong> Beispiel an. Es soll die Möglichkeit bieten,<br />
die Summe von verschie<strong>de</strong>nen Zahlen zu bil<strong>de</strong>n.<br />
41
Was erkennen wir? Es existieren 3 Metho<strong>de</strong>n mit Namen summe. Sie liefern<br />
jeweils die Summe <strong>de</strong>r übergebenen Argumente an <strong>de</strong>n Aufrufer zurück. Die<br />
Frage, die sich uns stellt ist: „Wie weit wollen wir das Spiel noch treiben?“ Ich<br />
meine damit die Anzahl <strong>de</strong>r überla<strong>de</strong>nen Metho<strong>de</strong>n, um auf möglichst viele<br />
Möglichkeiten in <strong>de</strong>r Anzahl <strong>de</strong>r Argumente reagieren zu können. Es gibt eine<br />
Alternative. Dazu existiert ein spezieller Parameter, <strong>de</strong>r durch das Schlüsselwort<br />
params gekennzeichnet wird.<br />
42
Die Konsolen-Ausgabe lautet:<br />
Summe ist: 0<br />
Summe ist: 21<br />
Summe ist: 10<br />
Summe ist: 17<br />
Durch <strong>de</strong>n Zusatz von params wird aus <strong>de</strong>m Parameter ein Array, auf das, wie<br />
schon gelernt, zugegriffen wer<strong>de</strong>n kann. Die foreach-Schleife spricht je<strong>de</strong>s<br />
Element <strong>de</strong>s Arrays an und in Zeile 17 wird die Summe gebil<strong>de</strong>t. Zeile 19 liefert<br />
das Ergebnis an <strong>de</strong>n Aufrufer zurück. Wie Sie in Zeile 27 sehen, können auch<br />
keine Argumente übergeben wer<strong>de</strong>n.<br />
43
Konstruktoren (spezialisierte Metho<strong>de</strong>n)<br />
Sie bekommen als Captain ihres neuen Raumschiffs meinSpee<strong>de</strong>r die Aufgabe,<br />
eine nahe gelegene befreun<strong>de</strong>te Kolonie aufzusuchen, in <strong>de</strong>r es einen<br />
feindlichen Übergriff aus <strong>de</strong>m Nachbarsektor gegeben hat. Natürlich verlassen<br />
sie sich darauf, dass ihre Bewaffnung in Form von Photonentorpedos in<br />
ausreichen<strong>de</strong>r Stückzahl zur Verfügung steht. Auf halber Strecke müssen sie<br />
sich lei<strong>de</strong>r vom Gegenteil überzeugen lassen. Wie konnte das passieren? Der<br />
folgen<strong>de</strong> Source-Co<strong>de</strong> konzentriert sich nur auf die Instanzvariable<br />
intPhotonentorpedos und lässt die restlichen Eigenschaften außen vor.<br />
44
Die Konsolen-Ausgabe bringt es ans Tageslicht:<br />
Anzahl <strong>de</strong>r Torpedos: 0<br />
Es wur<strong>de</strong> in <strong>de</strong>r Hektik einfach vergessen, die Metho<strong>de</strong> setPhotonentorpedos<br />
nach <strong>de</strong>m Generieren <strong>de</strong>s Raumschiffs mit entsprechen<strong>de</strong>r Anzahl aufzurufen.<br />
Wie können wir dieses Manko beheben? Am sichersten wäre ein<br />
Automatismus, <strong>de</strong>r nach <strong>de</strong>r Instanziierung <strong>de</strong>s Objektes eine bestimmte<br />
Metho<strong>de</strong> aufruft, die eine Vorinitialisierung vornimmt, so dass z.B. die<br />
Instanzvariablen einen <strong>de</strong>finierten, initialen Startwert bekommen. Das Problem,<br />
welches uns dabei entgegenschlägt ist aber: Je<strong>de</strong>r Programmierer könnte diese<br />
Metho<strong>de</strong> an<strong>de</strong>rs benennen. Wie stellen wir aufgrund dieser Problematik die<br />
Initialisierung <strong>de</strong>s Objektes sicher? Die Antwort ist recht simpel. Die Metho<strong>de</strong><br />
besitzt <strong>de</strong>n gleichen Namen wie die Klasse und wird somit zum Konstruktor.<br />
Die wörtliche Übersetzung aus <strong>de</strong>m Englischen heißt „Bauherr“.<br />
Er wird bei <strong>de</strong>r Instanziierung eines Objektes automatisch aufgerufen.<br />
Die Syntax lautet:<br />
Zugriffstyp Klassenname([Parameterliste])<br />
{<br />
}<br />
// Anweisung(en)<br />
Konstruktoren besitzen übrigens keinen Rückgabetyp (void inbegriffen).<br />
45
Dotty: „Das verstehe ich nicht. Ein Konstruktor ist doch auch eine art<br />
Metho<strong>de</strong>, die unter gewissen Umstän<strong>de</strong>n eine Berechnung durchführen und<br />
das Ergebnis an <strong>de</strong>n Aufrufer zurückliefern kann.“<br />
Und das ist genau <strong>de</strong>r Kasus Knacktus! Wodurch bzw. durch wen wird ein<br />
Konstruktor aufgerufen? Nicht etwa durch einen expliziten Aufruf von außen.<br />
Der Aufruf erfolgt bei <strong>de</strong>r Instanziierung, also zum Zeitpunkt <strong>de</strong>r<br />
Objektgenerierung. An wen sollte da ein Rückgabewert zurück geliefert<br />
wer<strong>de</strong>n? Schauen Sie sich <strong>de</strong>n modifizierten Source-Co<strong>de</strong> inklusive<br />
Konstruktor an:<br />
46
Die Konsolen-Ausgabe lautet:<br />
Konstruktor-Aufruf.<br />
Anzahl <strong>de</strong>r Torpedos: 2500<br />
Konstruktorüberladung<br />
Das Überla<strong>de</strong>n eines Konstruktors ist vergleichbar mit <strong>de</strong>m einer Metho<strong>de</strong>. Wir<br />
haben schon gesehen, dass mit einem Konstruktor ein Objekt initialisiert<br />
wer<strong>de</strong>n kann. Durch unterschiedliche Konstruktoren haben wir jetzt die<br />
Möglichkeit, verschie<strong>de</strong>ne Objekte mit abweichen<strong>de</strong>n Startwerten bei <strong>de</strong>r<br />
Instanziierung zu versehen. Der parameterlose Konstruktor aus <strong>de</strong>m letzten<br />
Beispiel sorgte dafür, dass die Bestückung bezüglich <strong>de</strong>r Anzahl <strong>de</strong>r<br />
Photonentorpedos automatisch auf <strong>de</strong>n Wert 2500 gesetzt wur<strong>de</strong>. Überla<strong>de</strong>n wir<br />
<strong>de</strong>n Konstruktor mit <strong>de</strong>r folgen<strong>de</strong>n Definition<br />
können wir mit <strong>de</strong>r Zeile<br />
unser Raumschiff entsprechend bestücken.<br />
48
Standardkonstruktor<br />
Sollten Sie keinen Konstruktor <strong>de</strong>finiert haben, übernimmt <strong>C#</strong> für Sie die<br />
Aufgabe und generiert automatisch einen parameterlosen Standardkonstruktor.<br />
Er ist u.a. dafür verantwortlich, dass die in <strong>de</strong>r Klasse aufgeführten<br />
Instanzvariablen mit einem Standardwert initialisiert wer<strong>de</strong>n. Wenn jedoch ein<br />
eigener parametrisierter Konstruktor hinzufügt wird, ist <strong>de</strong>r Standardkonstruktor<br />
nicht mehr existent. Sie müssen ihn notfalls explizit hinzufügen. Der folgen<strong>de</strong><br />
Co<strong>de</strong> enthält einen parametrisierten Konstruktor (Zeile 14 bis 17), jedoch keine<br />
explizite Definition für <strong>de</strong>n Standardkonstruktor.<br />
49
Die Schlangenlinien in Zeile 29 weisen uns unbarmherzig auf einen erkannten<br />
Fehler hin.<br />
Das be<strong>de</strong>utet, dass es keinen Konstruktor gibt, <strong>de</strong>r 0 Argumente annimmt,<br />
sprich: <strong>de</strong>r Standardkonstruktor fehlt. Falls er sowieso nichts machen soll, fügen<br />
Sie einfach folgen<strong>de</strong>n Co<strong>de</strong> ein:<br />
Konstruktor ruft Konstruktor<br />
Existieren mehrere überla<strong>de</strong>ne Konstruktoren, so kann es sinnvoll sein, wenn<br />
ein Konstruktor <strong>de</strong>n an<strong>de</strong>ren aufruft. Wird <strong>de</strong>r parameterlose Konstruktor<br />
aufgerufen, können wir ihn ihm eine Objektinitialisierung mit vor<strong>de</strong>finierten<br />
Werten festlegen. Existiert ein überla<strong>de</strong>ner, parametrisierter Konstruktor, <strong>de</strong>r<br />
ebenfalls für die Objektinitialisierung erstellt wur<strong>de</strong>, kann <strong>de</strong>r parameterlose<br />
Konstruktor <strong>de</strong>n parametrisierten ausführen, um gleichlauten<strong>de</strong>n Co<strong>de</strong> in bei<strong>de</strong>n<br />
Konstruktoren zu vermei<strong>de</strong>n. Schauen Sie sich das folgen<strong>de</strong> Beispiel an:<br />
50
Der parameterlose Konstruktor in Zeile 16 wird durch die Objektgenerierung<br />
aus Zeile 33 aufgerufen. Er führt seinerseits durch das Schlüsselwort this <strong>de</strong>n<br />
parametrisierten Konstruktor aus Zeile 21 aus, in <strong>de</strong>m er ihm die Werte in Form<br />
einer Arumentenliste (250, 300.5, 150.4) übergibt. Sie sehen auch, dass <strong>de</strong>r<br />
Konstruktorrumpf zwischen <strong>de</strong>n Zeilen 17 und 19 leer ist, da die Initialisierung<br />
an an<strong>de</strong>rer Stelle vorgenommen wird.<br />
51
this<br />
Sie erinnern sich bestimmt an die Metho<strong>de</strong> setDimension, mit <strong>de</strong>ren Hilfe wir<br />
die gekapselten Instanzvariablen dblLaenge und dblBreite initialisiert haben.<br />
Die Parameter dblL und dblB waren, wie Sie sicher vermutet haben, eine<br />
Abkürzung <strong>de</strong>r Instanzvariablen, um <strong>de</strong>ren Verwendungszweck <strong>de</strong>utlicher zu<br />
machen. Was spricht aber dagegen, wenn wir <strong>de</strong>n Parametern die gleichen<br />
Namen vergeben, wie die <strong>de</strong>r Instanzvariablen?<br />
Wenn Sie sich die Zuweisungen in <strong>de</strong>n Zeilen 18 und 19 anschauen, sollten bei<br />
Ihnen die Alarmglocken läuten. Syntaktisch gesehen ist alles in bester Ordnung,<br />
doch was wollen wir eigentlich bezwecken? Die übergebenen Argumente sollen<br />
in <strong>de</strong>n Parametern <strong>de</strong>r Metho<strong>de</strong> gespeichert und mittels Zuweisung an die<br />
privaten Instanzvariablen übergeben wer<strong>de</strong>n. Innerhalb <strong>de</strong>r Metho<strong>de</strong> wird direkt<br />
52
auf die <strong>de</strong>finierten Parameter dblLaenge bzw. dblBreite zugegriffen. Wie aber<br />
teilen wir <strong>de</strong>m Compiler mit, dass er auf die gleich lauten<strong>de</strong>n Instanzvariablen<br />
zugreifen soll? Jetzt kommt this zum Einsatz. Es dienst als Referenz auf das<br />
aktuelle Objekt. Modifizieren wir also die Zeilen 18 und 19 wie folgt:<br />
Auf <strong>de</strong>r linken Seite <strong>de</strong>r Zuweisungen wur<strong>de</strong>n die Variablennamen um das<br />
Schlüsselwort this erweitert. In dieser Kombination bezieht sich <strong>de</strong>r Ausdruck<br />
nicht mehr auf die Parameter <strong>de</strong>r Metho<strong>de</strong>, son<strong>de</strong>rn auf die Instanzvariablen.<br />
Garbage Collection<br />
53
Generieren wir über das Schlüsselwort new ein neues Objekt, wird ihm auf <strong>de</strong>m<br />
Heap zur Laufzeit ein Stückchen von freiem Speicher zugewiesen. Wie wir alle<br />
wissen, ist unser lokal zur Verfügung stehen<strong>de</strong>r Speicher zwar in <strong>de</strong>n letzten<br />
Jahren stetig gewachsen, doch <strong>de</strong>r Vorrat ist nicht unerschöpflich. In einer<br />
Anwendung wer<strong>de</strong>n Objekte dynamisch erstellt, können aber nach einer<br />
gewissen Zeit u.U. nicht mehr von Nutzen sein, da sie nicht mehr referenziert<br />
wer<strong>de</strong>n. Können wir ein solches Objekt nicht mehr über einen gültigen Verweis<br />
ansprechen, ist es nutzlos gewor<strong>de</strong>n und blockiert <strong>de</strong>n kostbaren Speicher, <strong>de</strong>r<br />
an an<strong>de</strong>rer Stelle benötigt wird. Die Frage, die sich uns stellt ist: „Wie<br />
entledigen wir uns <strong>de</strong>r Objektleichen?“ Tja, da können wir gar nichts machen.<br />
Nein wirklich, <strong>de</strong>nn das .NET Framework ist mit einer automatischen<br />
Speicherverwaltung ausgerüstet, welche sich <strong>de</strong>r verwaisten Objekte annimmt.<br />
Der Fachbegriff hierfür ist Garbage Collection (GC). Sie ist Bestandteil <strong>de</strong>r<br />
Common Language Runtime (CLR), arbeitet als Mülldienstleitster auf<br />
Speicherebene im Hintergrund und spürt alle nicht mehr benötigten Objekte auf,<br />
die daraufhin aus <strong>de</strong>m Speicher entfernt wer<strong>de</strong>n, damit dieser bereit für die<br />
Aufnahme neuer Objekte ist. Danach erfolgt eine Reorganisation. Die GC ist<br />
ständig bestrebt, dass immer genug Speicher-Ressourcen für künftige<br />
Anfor<strong>de</strong>rungen zur Verfügung stehen und kein Engpass aufgrund von<br />
Speicherleichen (Memory Leaks) entsteht.<br />
Dotty: „Das be<strong>de</strong>utet also, dass sie sofort zuschlägt und mit <strong>de</strong>m großen<br />
Besen vorbeikommt, wenn ein Objekt nicht mehr benötigt wird.“<br />
Dem ist nicht so. Die Garbage Collection legt ein, wie man so schön sagt, nicht<br />
<strong>de</strong>terministisches Verhalten an <strong>de</strong>n Tag, was be<strong>de</strong>utet, dass es nicht<br />
vorhersehbar ist, wann sie zuschlägt. Wür<strong>de</strong> sie sofort in Aktion treten, wenn<br />
auf ein Objekt kein gültiger Verweis mehr existierte, müsste eines natürlich<br />
sicher gestellt sein: Die Garbage Collection muss ständig aktiv sein. Sie können<br />
sich sicherlich vorstellen, dass das mit einer gewissen Rechenleistung<br />
verbun<strong>de</strong>n ist, die bei stetiger Ausführung zu Performanceeinbußen führt. Es<br />
gibt aber <strong>de</strong>nnoch ein Kriterium, das ihn dazu bewegt, aus seinem normalen<br />
Trott heraus, seine Aufräumarbeit zu beginnen. Wenn die freien<br />
Speicherressourcen ein gewissen Wert unterschreiten, so dass ein Engpass<br />
entstün<strong>de</strong>, wir die Suche gestartet.<br />
54
Destruktoren<br />
Sie haben die Baumeister <strong>de</strong>r Objekte, die Konstruktoren, kennen gelernt. Dazu<br />
gibt es ein passen<strong>de</strong>s Gegenstück. Den Destruktor. Es ist eine Metho<strong>de</strong>, die<br />
kurz bevor die Garbage Collection das Objekt aus <strong>de</strong>m Speicher entfernt,<br />
aufgerufen wird. Sie trägt <strong>de</strong>n gleichen Namen wie die Klasse, ganz gleich <strong>de</strong>m<br />
<strong>de</strong>r Konstruktor<strong>de</strong>finition, jedoch mit <strong>de</strong>m vorangestellten Til<strong>de</strong>-Zeichen ~.<br />
Der Destruktor besitzt keinen Rückgabetyp.<br />
Die Syntax lautet:<br />
~Klassenname()<br />
{<br />
}<br />
// Anweisung(en)<br />
Platzieren Sie <strong>de</strong>n Co<strong>de</strong> innerhalb <strong>de</strong>r Metho<strong>de</strong>, <strong>de</strong>r kurz vor <strong>de</strong>r Entfernung <strong>de</strong>s<br />
Objektes aus <strong>de</strong>m Speicher ausgeführt wer<strong>de</strong>n soll.<br />
55
Zeile<br />
Erklärung<br />
13 - 16 Konstruktor<strong>de</strong>finition<br />
18 - 21 Destruktor<strong>de</strong>finition<br />
27 Instanziierung <strong>de</strong>r Objektes meinSpee<strong>de</strong>r (Impliziter Aufruf <strong>de</strong>s Konstruktors)<br />
56
28 Objektfreigabe mittels Zuweisung durch null. Nach dieser Anweisung wird auf das<br />
Drücken <strong>de</strong>r Enter - Taste gewartet. Sie sehen, dass zu diesem Zeitpunkt <strong>de</strong>r<br />
Destruktor noch nicht aufgerufen wur<strong>de</strong>. (Keine Konsolenausgabe „Destruktoraufruf.“)<br />
34 Explizites Anstoßen <strong>de</strong>s Garbage Collectors (Aufruf <strong>de</strong>s Destruktors)<br />
Tab. 9: Co<strong>de</strong>-Erläuterungen<br />
Es existiert eine statische Metho<strong>de</strong> GC.Collect(), mit ihrer Hilfe die<br />
Garbage Collection explizit angestoßen wer<strong>de</strong>n kann.<br />
Kommen Sie jetzt bitte nicht auf die I<strong>de</strong>e, diese Metho<strong>de</strong> - für alle Fälle -<br />
mehrfach in Ihrer Anwendung aufzurufen, nur damit alle nicht mehr benötigten<br />
Objekte sofort gelöscht wer<strong>de</strong>n. Die Konsequenz dürfte Ihnen bekannt sein.<br />
Dotty: „Was passiert eigentlich, wenn ein einzelnes Objekt mehrere Verweise<br />
besitzt und nur einer davon wegfällt?“<br />
Existieren mehrere Verweise auf ein Objekt, so wird dieses von <strong>de</strong>r Garbage<br />
Collection nur erfasst, wenn <strong>de</strong>r letzte Verweis verschwun<strong>de</strong>n ist. Fügen wir<br />
eine zusätzliche Co<strong>de</strong>zeile (Zeile 29) ein, die eine zweite Referenz auf das<br />
Objekt generiert,<br />
und kappen <strong>de</strong>n ersten Verweis meinSpee<strong>de</strong>r in Zeile 30, verbleibt immer noch<br />
<strong>de</strong>r Verweis <strong>de</strong>inSpee<strong>de</strong>r, um auf das Objekt zuzugreifen. Die Garbage<br />
Collection wird bei diesem Objekt nicht aktiv.<br />
57
Vielleicht trägt das folgen<strong>de</strong> Bild auch ein wenig zum Verständnis bei.<br />
Eigenschaften<br />
Wenn es darum ging, auf eine geschützte, mit private <strong>de</strong>klarierte<br />
Instanzvariable zuzugreifen, haben wir jeweils eine Metho<strong>de</strong> zum Abfragen<br />
bzw. zum Modifizieren genutzt. Um die bei<strong>de</strong>n namentlich zu unterschei<strong>de</strong>n,<br />
nutzten wir das Präfix get (z.B. getBesatzung) für das Abfragen und set<br />
(z.B. setBesatzung) für das Modifizieren.<br />
58
Es wäre bestimmt einfacher und wünschenswert, müssten wir uns nicht zwei<br />
unterschiedliche Namen für <strong>de</strong>n Zugriff auf die Instanzvariable merken. Da wir<br />
die Kapselung <strong>de</strong>r Daten nicht aufweichen wollen, ermöglichen wir <strong>de</strong>n Zugriff<br />
auf die geschützten, mit private <strong>de</strong>klarierten Instanzvariablen nur über<br />
öffentliche Metho<strong>de</strong>n. Das ermöglicht uns, wie schon erwähnt, eine Kontrolle,<br />
<strong>de</strong>r an das Objekt herangetragenen Daten und bewart die Objektintegrität. In <strong>C#</strong><br />
gibt es einen speziellen Typ von Metho<strong>de</strong>: Die Eigenschaft (engl. Property). Die<br />
Syntax lautet:<br />
59
Zugriffstyp Rückgabetyp Name<br />
{<br />
get<br />
{<br />
// Anweisung(en)<br />
}<br />
set<br />
{<br />
// Anweisung(en)<br />
}<br />
}<br />
60
Metho<strong>de</strong><br />
get<br />
set<br />
Erklärung<br />
Liefert ein Wert zurück<br />
Modifiziert einen Wert und nutzt die implizit <strong>de</strong>finierte Variable value<br />
Die Metho<strong>de</strong>n wer<strong>de</strong>n auch als getter- bzw. setter-Metho<strong>de</strong>n bezeichnet. Wie<br />
Sie in Zeile 26 sehen, erfolgt die Wertzuweisung mit <strong>de</strong>m Zuweisungsoperator<br />
=, <strong>de</strong>r auch bei Variablenzuweisungen Verwendung fin<strong>de</strong>t. Die Abfrage <strong>de</strong>r<br />
Instanzvariablen, die in Zeile 27 statt fin<strong>de</strong>t, erfolgt über <strong>de</strong>nselben Ausdruck.<br />
Zusammenfassend können wir sagen, dass das Verhalten von Eigenschaften<br />
mit <strong>de</strong>r von öffentlichen, mit public <strong>de</strong>klarierten Instanzvariablen zu<br />
vergleichen ist.<br />
Haben wie eine Eigenschaft erstellt, wird diesem Element im Kontext <strong>de</strong>r<br />
Klassenumgebung ein neues Icon zugewiesen.<br />
Icon<br />
Erklärung<br />
Tab. 10:<br />
Signalisiert, dass es sich um eine Eigenschaft (Property) han<strong>de</strong>lt<br />
Iconerklärungen<br />
Führen Sie sich <strong>de</strong>n Unterschied in <strong>de</strong>n Aufrufen von Metho<strong>de</strong>n und<br />
Eigenschaften noch einmal vor Augen:<br />
Metho<strong>de</strong><br />
meinSpee<strong>de</strong>r.setBesatzung(400);<br />
Eigenschaft meinSpee<strong>de</strong>r.Besatzung = 400;<br />
Read-Only<br />
Sie können <strong>de</strong>n Zugriff auf die privaten Daten über eine Eigenschaft<br />
reglementieren, in <strong>de</strong>m Sie entwe<strong>de</strong>r die get- o<strong>de</strong>r die set-Metho<strong>de</strong> weglassen.<br />
61
Ist nur eine get-Metho<strong>de</strong> vorhan<strong>de</strong>n, haben Sie auch nur die Möglichkeit, Daten<br />
zu lesen. Wir nennen dies Read-Only (nur lesen).<br />
Write-Only<br />
Im Gegensatz dazu können wir mit <strong>de</strong>m alleinigen Vorhan<strong>de</strong>nsein einer set-<br />
Metho<strong>de</strong> nur das Modifizieren gestattet. Es wird Write-Only (nur schreiben)<br />
genannt.<br />
Dotty: „Ich verstehe zwar die Unterschie<strong>de</strong> in <strong>de</strong>r Implementierung, doch für<br />
welche Variante soll ich mich entschei<strong>de</strong>n?“<br />
Die Antwort darauf ist nicht einfach und im En<strong>de</strong>ffekt muss je<strong>de</strong>r für sich<br />
entschei<strong>de</strong>n, ob er <strong>de</strong>n Schreib/Lesezugriff auf private Daten lieber über zwei<br />
Metho<strong>de</strong>n mit unterschiedlichen Namen o<strong>de</strong>r über eine einzige Eigenschaft<br />
realisieren möchte. Falls Sie sich grundsätzlich für Metho<strong>de</strong>n entschei<strong>de</strong>n,<br />
sollten Sie be<strong>de</strong>nken, dass die Anzahl <strong>de</strong>r Objekt-Member natürlich verdoppelt<br />
wird. Das trägt u.U. nicht gera<strong>de</strong> zur besseren Übersicht bei.<br />
Konstanten<br />
Möchten Sie zu Beginn einer Anwendung einen o<strong>de</strong>r mehrere Werte festlegen,<br />
die zur Laufzeit nicht geän<strong>de</strong>rt wer<strong>de</strong>n dürfen, sollten Sie Konstanten<br />
<strong>de</strong>finieren. Die Kreiszahl Pi wür<strong>de</strong> in diese Kategorie fallen. Stellen Sie sich<br />
vor, dass durch irgen<strong>de</strong>inen dummen Zufall Pi einen an<strong>de</strong>ren Wert als<br />
62
3,141592... bekommen wür<strong>de</strong>. Nicht auszu<strong>de</strong>nken, welchen Aufstand die Kreise<br />
anzetteln wür<strong>de</strong>n. Die Syntax lautet:<br />
Zugriffstyp const Datentyp Name = Wert;<br />
Proben Sie <strong>de</strong>nnoch <strong>de</strong>n Aufstand und wollen mit aller Macht eine Än<strong>de</strong>rung,<br />
bekommen Sie die Quittung:<br />
Icon<br />
Erklärung<br />
Tab. 11:<br />
Signalisiert, dass es sich um eine Konstante han<strong>de</strong>lt<br />
Iconerklärungen<br />
Vererbung<br />
Grundlagen<br />
Kommen wir jetzt zu einem Kapitel, das sich mit <strong>de</strong>r Vererbung in <strong>C#</strong> befasst.<br />
Es geht dabei um ein Konzept in <strong>de</strong>r OOP, welches es ermöglicht, vorhan<strong>de</strong>nen<br />
Co<strong>de</strong> einer Klasse in einer abgeleiteten, Klasse wie<strong>de</strong>r zu verwen<strong>de</strong>n. Die<br />
abgeleitete Klasse - wir nennen sie mal Kindklasse - erbt sozusagen die<br />
Metho<strong>de</strong>n und Eigenschaften <strong>de</strong>r Basisklasse. Wir arbeiten also nach <strong>de</strong>m<br />
Motto: „Warum das Rad neu erfin<strong>de</strong>n?“ Nutzen wir die vorhan<strong>de</strong>ne<br />
63
Funktionalität einer Basisklasse und erweitern diese nach unseren Wünschen,<br />
um die notwendigen Aspekte in einer spezialisierten Kindklasse. Sehen wir uns<br />
das wie<strong>de</strong>r am Beispiel unserer Raumflotte an. Sie besteht natürlich nicht nur<br />
aus Kampfschiffen. Da gibt es z.B. noch Transport- o<strong>de</strong>r Forschungsschiffe. Die<br />
Frage, die wir uns stellen sollten ist: „Welche Eigenschaften haben alle Schiffe<br />
gemeinsam und in welchen Punkten unterschei<strong>de</strong>n sie sich?“ Die folgen<strong>de</strong>n<br />
Auflistungen <strong>de</strong>r Eigenschaften beschränken sich nur auf das Wesentlichste, um<br />
das Beispiel so knapp und einfach wie möglich zu halten:<br />
Kampfschiff Transportschiff Forschungsschiff<br />
Triebwerke Triebwerke Triebwerke<br />
Besatzung Besatzung Besatzung<br />
Photonentorpedos Lagerräume Forschungseinrichtung<br />
Quantentorpedos<br />
Andockschleusen<br />
Tab. 12: Raumschiff-Typen<br />
Ableiten von einer Basisklasse<br />
Sie sehen, dass die ersten bei<strong>de</strong>n Zeilen die Eigenschaften enthalten, die bei<br />
allen drei Schiffs-Typen gleich lauten. Alle nachfolgen<strong>de</strong>n Zeilen beschreiben<br />
Spezialisierungen <strong>de</strong>s jeweiligen Schiffs-Typ. Es liegt also nahe, die<br />
Gemeinsamkeiten, sprich die Schnittmenge, in eine Basisklasse auszulagern,<br />
um sie nicht in je<strong>de</strong>r Raumschiffklasse gleichermaßen zu benennen<br />
(Redundanter Co<strong>de</strong>). Etwaige Än<strong>de</strong>rungen bzw. Verbesserungen in <strong>de</strong>r<br />
Basisklasse kommen durch diese Auslagerung allen abgeleiteten Klassen zu<br />
Gute.<br />
64
Wir können die Abhängigkeit bzw. hierarchische Struktur in einem<br />
Klassendiagramm darstellen. Die Spitze <strong>de</strong>s Pfeils zeigt dabei auf die<br />
Basisklasse. (UML-Notation)<br />
Die Kodierung <strong>de</strong>r Basisklasse lautet <strong>de</strong>mnach:<br />
65
So weit so gut. Nun erschaffen wir exemplarisch ein Raumschiff, welches sich<br />
<strong>de</strong>r Eigenschaften <strong>de</strong>r Basisklasse bedient, jedoch einige ihm eigene<br />
Spezialisierungen hinzufügt. Der neue Typ soll Kampfschiff lauten. Bei <strong>de</strong>r<br />
Klassen<strong>de</strong>finition müssen wir aber zum Ausdruck bringen, dass die Ableitung<br />
von <strong>de</strong>r Basisklasse Raumschiff erfolgen soll. Das wird mit <strong>de</strong>m<br />
Doppelpunktoperator : und <strong>de</strong>r nachfolgend aufgeführten Basisklasse erreicht.<br />
Generieren Sie jetzt ein neues Kampfschiff, so bietet Ihnen IntelliSense die<br />
Instanzvariablen intBesatzung und intTriebwerke <strong>de</strong>r Basisklasse<br />
Raumschiff ebenso wie intPhotonentorpedos und<br />
intQuantentorpedos <strong>de</strong>r Kindklasse Kampfschiff zur Auswahl an.<br />
66
Eine abgeleitete Klasse ist in <strong>de</strong>r Regel spezialisierter und enthält mehr<br />
Funktionalität als ihre Basisklasse. Sie erhält Zugriff auf alle nicht privaten<br />
Member <strong>de</strong>r Basisklasse und auf alle in ihr selbst <strong>de</strong>finierten.<br />
Dotty: „Mit <strong>de</strong>r Kapselung ist es bei diesem Beispiel ja nicht weit her. Alle<br />
Eigenschaften sind direkt zu Manipulieren, so dass die Objektintegrität<br />
potentiell gefähr<strong>de</strong>t ist.“<br />
Vollkommen korrekt erkannt! Das folgen<strong>de</strong> Beispiel zeigt die Modifikation <strong>de</strong>s<br />
Programms dahingehend, dass die Kapselung wie<strong>de</strong>r aktiviert wird. Der Zugriff<br />
ist ausschließlich <strong>de</strong>n öffentlichen Metho<strong>de</strong>n gestattet. Die als private<br />
<strong>de</strong>klarierten Member sind in <strong>de</strong>r abgeleiteten Klasse nicht sichtbar.<br />
67
IntelliSense zeigt uns, dass nur auf mit public <strong>de</strong>klarierte Member <strong>de</strong>r Basisbzw.<br />
Kindklasse zugegriffen wer<strong>de</strong>n kann.<br />
Kindklasse ruft Basisklasse<br />
Dotty: „Wie können wir <strong>de</strong>r Kindklasse erlauben, direkt auf private Member<br />
<strong>de</strong>r Basisklasse zuzugreifen, ohne die Kapselung mit public aufzuweichen?“<br />
Wir stecken ein wenig in einer Zwickmühle. Der Zugriffsmodifizierer public ist<br />
zu lasch in seinen Restriktionen, wohingegen private es etwas zu weit treibt.<br />
Die Lösung <strong>de</strong>s Problems ist ein bisher unbekannter Zugriffsmodifizierer, <strong>de</strong>r<br />
diese Funktionalität ermöglicht. Er lautet: protected. Der folgen<strong>de</strong> Zugriff auf<br />
die private Instanzvariable intBesatzung <strong>de</strong>r Basisklasse führt zu einem<br />
Fehler:<br />
69
Durch die folgen<strong>de</strong> Modifikation <strong>de</strong>r Basisklasse wird dieser Fehler vermie<strong>de</strong>n<br />
und <strong>de</strong>r Zugriff über die Kindklasse erlaubt.<br />
Dotty: „Der Fehler ist zwar nicht mehr da, doch die genauen<br />
Zusammenhänge liegen für mich immer noch im verborgenen!“<br />
Wird ein Member mit <strong>de</strong>m Zugriffsmodifizierer protected <strong>de</strong>klariert, verhält es<br />
sich für die betroffene Klasse wie mit private <strong>de</strong>klariert. Es ist nur innerhalb <strong>de</strong>r<br />
Klasse sichtbar. Der Unterschied kommt beim Vererben zum tragen. Das<br />
vererbte protected Member ist in <strong>de</strong>r Kindklasse sichtbar und es kann darauf<br />
zugegriffen wer<strong>de</strong>n.<br />
70
Konstruktoren im Kontext <strong>de</strong>r Vererbung<br />
Besitzt eine Basisklasse einen o<strong>de</strong>r mehrere Konstruktoren, stellt sich uns die<br />
Frage, ob diese beim Vererbungsvorgang in die Kindklasse übernommen<br />
wer<strong>de</strong>n. Die Antwort lautet: Nein. Eine Basisklasse besitzt keine Informationen<br />
über die Member <strong>de</strong>r Kindklasse, was be<strong>de</strong>utet, dass die abgeleitete Klasse, falls<br />
notwendig, selbst für die Konstruktoren verantwortlich ist.<br />
Welcher Konstruktor wird wann aufgerufen?<br />
Besitzen sowohl die Basis- als auch die Kindklasse explizit <strong>de</strong>finierte<br />
Konstruktoren, fragen wir uns: „Wann wird welcher Konstruktor aufgerufen?“<br />
Schauen Sie sich das folgen<strong>de</strong>, ausschließlich mit Konstruktoren versehene<br />
Beispiel an:<br />
71
Die Konsolen-Ausgabe lautet:<br />
Konstruktoraufruf: Basisklasse ’Raumschiff’<br />
Konstruktoraufruf: Kindklasse ’Kampfschiff’<br />
Sie sehen, dass bei <strong>de</strong>r Objektgenerierung einer abgeleiteten Klasse in Zeile 33<br />
zuerst <strong>de</strong>r parameterlose Standardkonstruktor <strong>de</strong>r Basisklasse und danach <strong>de</strong>r,<br />
<strong>de</strong>r Kindklasse aufgerufen wird. Das ist eigentlich logisch, <strong>de</strong>nn die Basisklasse<br />
ist ebenso für ihren Teil <strong>de</strong>r Initialisierung verantwortlich, wie die Kindklasse.<br />
72
Der Aufruf <strong>de</strong>r Konstruktoren in <strong>de</strong>r Vererbungshierarchie beginnt stets bei <strong>de</strong>r<br />
Wurzel, also <strong>de</strong>r Basisklasse und erfolgt von dort aus weiter entlang <strong>de</strong>r<br />
Erblinie, bis hin zum jüngsten Spross. Aus Platzgrün<strong>de</strong>n habe ich mich in<br />
meinen Beispielen auf eine einzige Kindklasse beschränkt. Sie können jedoch<br />
die Kindklasse als neue Basisklasse für eine weitere Vererbung nutzen.<br />
Member ver<strong>de</strong>cken<br />
Wir haben schon gesehen, dass durch <strong>de</strong>n Vorgang <strong>de</strong>r Vererbung, alle Member<br />
<strong>de</strong>r Basisklasse (nicht die Konstruktoren und Destruktoren), die nicht mit<br />
private <strong>de</strong>klariert wur<strong>de</strong>n, ebenso in <strong>de</strong>r Kindklasse zur Verfügung stehen. Was<br />
passiert aber, wenn z.B. eine Metho<strong>de</strong> in <strong>de</strong>r Kindklasse <strong>de</strong>n gleichen Namen<br />
inklusive Parameterliste trägt, wie eine in ihrer Basisklasse?<br />
73
Sowohl in <strong>de</strong>r Basisklasse als auch in <strong>de</strong>r Kindklasse existiert eine Metho<strong>de</strong> mit<br />
Namen getID(). Starten Sie das Programm, so lautet die Konsolen-Ausgabe:<br />
ID: Kampfschiff.<br />
Die Metho<strong>de</strong> <strong>de</strong>r Basisklasse ist überschrieben wor<strong>de</strong>n. Die Frage, die Sie sich<br />
jetzt stellen sollten ist: „Ist das von mir so gewollt, o<strong>de</strong>r habe ich durch eine<br />
Unachtsamkeit <strong>de</strong>n gleichen Namen gewählt?“ Der Compiler erkennt dies und<br />
weist Sie mit einer Warnung auf diesen Umstand hin. Sehen Sie die<br />
Schlangenlinien unterhalb von getID() innerhalb <strong>de</strong>r Kindklasse? Der<br />
Hinweis besagt, dass <strong>de</strong>r vererbte Member getID() <strong>de</strong>r Basisklasse<br />
ausgeblen<strong>de</strong>t wird. Falls das so von Ihnen gewünscht ist, können Sie die<br />
74
Warnung dadurch verhin<strong>de</strong>rn, dass das Schlüsselwort new in <strong>de</strong>r Kindklasse<br />
verwen<strong>de</strong>t wird:<br />
Dotty: „Können wir jetzt nicht mehr auf die ver<strong>de</strong>ckte Metho<strong>de</strong> zugreifen?“<br />
Doch, das ich weiterhin möglich. Durch die Verwendung <strong>de</strong>s Schlüsselwortes<br />
base wird <strong>de</strong>r Zugriff auf die Basisklasse ermöglicht.<br />
75
Die Konsolen-Ausgabe lautet:<br />
ID: Raumschiff.<br />
Polymorphie<br />
Kommen wir jetzt zu einem Punkt, <strong>de</strong>r vielleicht ein paar Stirnrunzeln bei Ihnen<br />
hervorrufen wird. Übersetzt be<strong>de</strong>utet Polymorphie Vielgestaltigkeit. In <strong>C#</strong><br />
können in einer Basisklasse z.B. Metho<strong>de</strong>n existieren, die in <strong>de</strong>r abgeleiteten<br />
Kindklasse neu <strong>de</strong>finiert wer<strong>de</strong>n. Dadurch erlangen wir die Fähigkeit,<br />
76
estehen<strong>de</strong>n Programm-Co<strong>de</strong> ohne Anpassung bzw. Modifikation nutzen zu<br />
können, ihn jedoch nach unseren Bedürfnissen zu erweitern. Vererbung ist also<br />
ein zwingen<strong>de</strong>r Bestandteil für Polymorphie. Doch es gehört noch ein wenig<br />
mehr dazu.<br />
Dotty: „Haben wir Polymorphie nicht schon längst in unserem letzten<br />
Beispiel mit <strong>de</strong>r neu <strong>de</strong>finierten Metho<strong>de</strong> erreicht? Die neue Metho<strong>de</strong> in <strong>de</strong>r<br />
Kindklasse hat die bestehen<strong>de</strong> <strong>de</strong>r Basisklasse mit neuer Funktionalität<br />
abgelöst.“<br />
Das ist eben nicht <strong>de</strong>r Fall. Schauen wir uns das nun folgen<strong>de</strong> Beispiel an,<br />
welches nichts mit Polymorphie zu tun hat.<br />
Die Basisklasse Raumschiff dient als Ausgangspunkt für unsere geplanten<br />
Weiterentwicklungen Kampfschiff und Transportschiff.<br />
77
Die Kindklasse Kampfschiff wird von <strong>de</strong>r Basisklasse Raumschiff abgeleitet.<br />
Die Kindklasse Transportschiff wird von <strong>de</strong>r Basisklasse Raumschiff abgeleitet.<br />
78
Im Hauptprogramm legen wir ein Array mit drei Elementen an, welches die<br />
Raumschiff-Objekte aufnehmen kann (Zeile 58).<br />
Dotty: „Jetzt bin ich vollends verwirrt! Wir erzeugen Referenzen vom Typ<br />
Raumschiff, die aber auf unterschiedliche Objekte (Kampfschiff bzw.<br />
Transportschiff) zeigen. Wie soll das <strong>de</strong>nn gehen?“<br />
Wir halten fest, dass ein Kampfschiff bzw. ein Transportschiff immer auch ein<br />
Raumschiff ist. Das wird in <strong>de</strong>r OOP eine ist-ein-Beziehung genannt. Deshalb<br />
kann die Referenzvariable Raumschiff sowohl auf ein Kampfschiff, als auch auf<br />
ein Transportschiff weisen. Natürlich hat die Basisklasse keine Kenntnis von<br />
<strong>de</strong>n Membern <strong>de</strong>r Kindklasse(n) und kann aus diesem Grun<strong>de</strong> nicht auf sie<br />
zugreifen. Doch schauen wir uns die Konsolen-Ausgabe einmal genauer an:<br />
ID: Raumschiff. Fighter<br />
ID: Raumschiff. Transporter<br />
ID: Raumschiff. Raumschiff<br />
Was erkennen wir? Nun, die ID-Kennung lautet in je<strong>de</strong>m Fall für alle drei<br />
Raumschiffe Raumschiff. Ist das korrekt? Gemäß Programm-Co<strong>de</strong> zwar eine<br />
korrekte Ausgabe, doch nicht unbedingt gewollt. Es wird stets die Basisklassen-<br />
Implementierung <strong>de</strong>r Metho<strong>de</strong> getID() aufgerufen und nicht die<br />
79
entsprechen<strong>de</strong> in <strong>de</strong>r Kindklasse. Dieses Verhalten kommt <strong>de</strong>swegen zustan<strong>de</strong>,<br />
weil wir es mit einer statischen Bindung (early-binding) zu tun haben. Es steht<br />
zur Entwicklungszeit schon fest, welche Metho<strong>de</strong> an welches Objekt gebun<strong>de</strong>n<br />
ist. Doch wie erreichen wir <strong>de</strong>n korrekten Aufruf <strong>de</strong>r Kindklassen-Metho<strong>de</strong><br />
getID()?<br />
virtual und overri<strong>de</strong><br />
Die bei<strong>de</strong>n Schlüsselwörter virtual und overri<strong>de</strong> helfen unserem Programm-<br />
Co<strong>de</strong> auf die Beine. Sie bewirken, dass wir über eine Basisklassenreferenz auf<br />
eine Metho<strong>de</strong> in <strong>de</strong>r abgeleiteten Kindklasse Zugriff nehmen können. Dies wird<br />
dynamische o<strong>de</strong>r späte Bindung (late-binding) genannt. Die Metho<strong>de</strong>nbindung<br />
an ein Objekt steht erst zur Laufzeit <strong>de</strong>s Programms zur Verfügung. Damit wir<br />
wahre Polymorphie erleben, müssen zwei Modifikationen vorgenommen<br />
wer<strong>de</strong>n:<br />
1. Die Basisklassen<strong>de</strong>finition muss mit <strong>de</strong>m Schlüsselwort virtual versehen<br />
wer<strong>de</strong>n<br />
2. Die Kindklassen<strong>de</strong>finition muss mit <strong>de</strong>m Schlüsselwort overri<strong>de</strong> versehen<br />
wer<strong>de</strong>n. Sie kennzeichnet das Überschreiben <strong>de</strong>r Basisklassen<strong>de</strong>finition<br />
Die Co<strong>de</strong>-Än<strong>de</strong>rungen lauten im Detail:<br />
Je<strong>de</strong> betroffene Metho<strong>de</strong> <strong>de</strong>r Kindklasse muss mit <strong>de</strong>m Schlüsselwort overri<strong>de</strong><br />
versehen wer<strong>de</strong>n<br />
80
Starten wir jetzt das Programm, erhalten wir die Konsolen-Ausgabe:<br />
ID: Kampfschiff. Fighter<br />
ID: Transportschiff. Transporter<br />
ID: Raumschiff. Raumschiff<br />
Sie sehen, dass jetzt die entsprechen<strong>de</strong>n Metho<strong>de</strong>n (ID: ...) <strong>de</strong>r Kindklassen<br />
aufgerufen wer<strong>de</strong>n. Fassen wir noch einmal die wichtigen Punkte für<br />
Polymorphie zusammen:<br />
♦ Die Basisklasse muss eine mit virtual <strong>de</strong>klarierte Metho<strong>de</strong> besitzen<br />
♦ In <strong>de</strong>r abgeleiteten Klasse wird die gleichnamige Metho<strong>de</strong> mit overri<strong>de</strong><br />
überschrieben<br />
♦ Der Aufruf erfolgt über eine Basisklassenreferenz <strong>de</strong>r Vererbungshierarchie<br />
81
Statische Klassen<br />
Sie haben sich sicherlich schon oft gefragt, warum die Metho<strong>de</strong> Main() mit <strong>de</strong>m<br />
Schlüsselwort static <strong>de</strong>finiert wur<strong>de</strong>. Eingangs habe ich kurz erwähnt, dass es<br />
nicht notwendig ist, für die Startmetho<strong>de</strong> Main() eine Instanz zu generieren. Es<br />
muss zu Beginn eines Programms einen initialen Mechanismus geben, von <strong>de</strong>m<br />
aus alle weiteren Aktionen ausgehen. Am Anfang haben wir ja noch kein<br />
einziges Objekt und Main wird als Einsprungpunkt genutzt. Sind wir erst<br />
einmal in Main angekommen, können natürlich Objekte generiert wer<strong>de</strong>n. Es<br />
gibt jedoch Anfor<strong>de</strong>rungen, die nicht notwendigerweise ein Objekt<br />
voraussetzten. Ein Beispiel hierfür ist die Klasse Math.<br />
Sie sehen in Zeile 18 <strong>de</strong>n Aufruf <strong>de</strong>r Metho<strong>de</strong> Sqrt (Square-Root = Quadrat-<br />
Wurzel) <strong>de</strong>r Klasse Math. Es war <strong>de</strong>mnach nicht notwendig zuvor eine Instanz<br />
<strong>de</strong>r Klasse Math zu bil<strong>de</strong>n, um die Metho<strong>de</strong> Sqrt nutzen zu können. Zu<strong>de</strong>m<br />
wur<strong>de</strong>n keine Instanzvariablen zur Berechnung benötigt und es existieren auch<br />
keine Metho<strong>de</strong>n, die über gemeinsame Instanzvariablen möglicherweise Daten<br />
austauschen. Warum also ein Objekt erschaffen, wenn nur die Metho<strong>de</strong>n zu<br />
nutzen sind? Schauen Sie sich das folgen<strong>de</strong> Beispiel an, dass eine statische<br />
Metho<strong>de</strong> innerhalb <strong>de</strong>r Klasse <strong>de</strong>finiert, in <strong>de</strong>r sich auch Main befin<strong>de</strong>t.<br />
82
Eleganter ist es natürlich, wenn Sie sich eine eigene Klasse schreiben, die alle<br />
für Sie wichtigen Metho<strong>de</strong>n zur Berechnung enthält. Das ist ungleich schwerer.<br />
Hier sehen Sie <strong>de</strong>n Co<strong>de</strong> für die Klassen<strong>de</strong>finition:<br />
83
Achten Sie darauf, dass Sie <strong>de</strong>n Zugriffsmodifizierer public nicht vergessen.<br />
An<strong>de</strong>rnfalls wird die Metho<strong>de</strong> implizit als private <strong>de</strong>klariert und Sie können sie<br />
nicht aufrufen. Der Aufruf <strong>de</strong>r Metho<strong>de</strong> ist nicht weiter schwierig. Sie müssen<br />
nur <strong>de</strong>n Klassennamen <strong>de</strong>m Metho<strong>de</strong>naufruf voransetzten.<br />
84
Konstruktoren<br />
Dotty: „So wie ich die Sache sehe, benötigen wir zu Nutzung <strong>de</strong>r statischen<br />
Klassenelemente kein Objekt. Gibt es <strong>de</strong>nn überhaupt Konstruktoren? Diese<br />
wer<strong>de</strong>n eigentlich durch das Schlüsselwort new aufgerufen, was aber in<br />
diesem Fall nicht möglich ist.“<br />
Wir können auch hier einen Konstruktor verwen<strong>de</strong>n. Dieser wird dann<br />
aufgerufen, bevor die erste statische Metho<strong>de</strong> aufgerufen wird o<strong>de</strong>r eine Instanz<br />
erstellt wur<strong>de</strong>.<br />
85
Die Zeile 14 ruft erstmalig die statische Metho<strong>de</strong> Mathe.Add() auf, was einen<br />
vorherigen Aufruf <strong>de</strong>s Konstruktors be<strong>de</strong>utet. Die Konsolen-Ausgabe lautet:<br />
Konstruktoraufruf.<br />
7<br />
54<br />
Be<strong>de</strong>nken Sie, dass es sich in diesem Fall um eine Klassen-Metho<strong>de</strong> han<strong>de</strong>lt,<br />
wogegen eine Metho<strong>de</strong> eines real existieren<strong>de</strong>n Objektes Objekt-Metho<strong>de</strong><br />
genannt wird.<br />
Eine Klasse kann sowohl Objekt-Metho<strong>de</strong>n als auch Klassen-Metho<strong>de</strong>n<br />
beinhalten. Sie sollten jedoch wissen, dass Klassen-Metho<strong>de</strong>n ausschließlich auf<br />
statische Klassenelemente (statische Variablen bzw. Klassen-Metho<strong>de</strong>n)<br />
zugreifen können. Das folgen<strong>de</strong> Beispiel einer Klassen<strong>de</strong>finition zeigt das<br />
Problem, dass die Klassen-Metho<strong>de</strong> getWert() auf eine Objekt-Variable<br />
zugreifen möchte. Der Zugriff ist jedoch nur über eine Objekt-Referenz<br />
(Verweis) möglich.<br />
Die Fehlermeldung lautet:<br />
86
Um dieses Zwitterverhalten von Klassen-Elementen und Objekt-Elementen zu<br />
vermei<strong>de</strong>n, ist es möglich, eine Klasse komplett statisch zu <strong>de</strong>klarieren. Das hat<br />
zur Folge, dass alle Klassen-Elemente ebenfalls statisch sein müssen. Erkennen<br />
Sie, was das wie<strong>de</strong>rum zur Folge hat? Es kann niemals eine einzige Instanz<br />
dieser Klasse erstellt wer<strong>de</strong>n.<br />
Dotty: „Ich sehe eigentlich keinen einzigen Grund, warum ich z.B. eine<br />
statische Klassen-Variable und eine Objekt-Metho<strong>de</strong> <strong>de</strong>klarieren sollte.“<br />
Stellen wir uns vor, dass wir eine Klasse erstellt haben, von <strong>de</strong>r mehrere Objekt-<br />
Instanzen erstellt wer<strong>de</strong>n müssen. Sie wollen aber die Übersicht behalten, wie<br />
viele Objekt-Instanzen erstellt wur<strong>de</strong>n. Es ist also ein Zähler von Nöten, <strong>de</strong>r die<br />
einzelnen generierten Instanzen aufaddiert. Wür<strong>de</strong>n wir eine Objekt-Variable<br />
innerhalb einer Klasse <strong>de</strong>klarieren, bekämen wir bei je<strong>de</strong>r neuen<br />
Objektinstanziierung eine neue unabhängige Variable. Aus diesem Grund<br />
nutzen wir eine Klassen-Variable, die mit static <strong>de</strong>klariert wur<strong>de</strong>. Sie ist Objekt<br />
unabhängig und nur <strong>de</strong>r Klasse verpflichtet und wird durch <strong>de</strong>n Konstruktor <strong>de</strong>r<br />
Klasse bei je<strong>de</strong>r Instanziierung (Zeile 14, 15 und 16) inkrementiert.<br />
87
Die Konsolen-Ausgabe lautet:<br />
Anzahl <strong>de</strong>r Instanzen: 3<br />
Lassen Sie uns die gewonnen Erkenntnisse zusammenfassen:<br />
♦ Der Zugriff auf statische Metho<strong>de</strong>n und Variablen erfolgt über <strong>de</strong>n<br />
Klassennamen. Das folgen<strong>de</strong> Beispiel zeigt <strong>de</strong>n Zugriff auf die Klassen-<br />
88
Metho<strong>de</strong> Add()<br />
♦ Statische Metho<strong>de</strong>n können nur auf statische Variablen bzw. statische<br />
Metho<strong>de</strong>n zugreifen. Auf Instanz-Variablen bzw. Instanz-Metho<strong>de</strong>n können<br />
sie nicht zugreifen. Dafür wäre eine Objekt-Referenz bzw. ein Verweis<br />
notwendig.<br />
♦ Statische Konstruktoren wer<strong>de</strong>n vor <strong>de</strong>m ersten Aufruf einer statischen<br />
Metho<strong>de</strong> o<strong>de</strong>r durch Instanziierung aufgerufen. Statische Konstruktoren<br />
erlauben keine Parameter bzw. Zugriffsmodifizierer.<br />
Globale Variablen<br />
Wir können mit statischen Klassen z.B. globale Variablen simulieren, die es in<br />
<strong>C#</strong> eigentlich gar nicht gibt. Fügen Sie einfach eine neue Klasse Ihrem Projekt<br />
hinzu und <strong>de</strong>klarieren die Variable(n) darin als statisch. Gehen Sie wie folgt<br />
vor, um eine neue Klasse <strong>de</strong>m aktuellen Projekt hinzuzufügen:<br />
89
1. Aktuelles Projekt markieren + rechte Maustaste<br />
2. Hinzufügen...<br />
3. Neues Element<br />
Im nachfolgen<strong>de</strong>n Dialog<br />
90
4. Die Klassenvorlage wählen<br />
5. Einen ein<strong>de</strong>utigen Namen vergeben<br />
6. Den Hinzufügen-Button klicken<br />
Im Projektmappen-Explorer erscheint jetzt die neue Klasse:<br />
91
Wechseln Sie mit einem Doppelklick auf <strong>de</strong>n Eintrag in die Co<strong>de</strong>-View <strong>de</strong>r<br />
Klasse und geben als Beispiel folgen<strong>de</strong>s ein:<br />
Anschließend können Sie in Ihrem Hauptprogramm auf die Klassenvariablen<br />
zugreifen, in<strong>de</strong>m Sie <strong>de</strong>n Klassennamen plus Variablennamen verwen<strong>de</strong>n.<br />
IntelliSense bietet die öffentlichen Variablen <strong>de</strong>r Klasse variables zur Auswahl<br />
an. Sie können an je<strong>de</strong>r Stelle Ihres Programms auf diese Art und Weise<br />
schreibend o<strong>de</strong>r lesend zugreifen.<br />
Main-Parameter<br />
Vielleicht haben sich einige von Ihnen die Metho<strong>de</strong> Main() ein wenig genauer<br />
angeschaut und sich gewun<strong>de</strong>rt, warum innerhalb <strong>de</strong>s run<strong>de</strong>n Klammernpaares<br />
etwas steht. Es sieht so aus, als ob wir <strong>de</strong>r Metho<strong>de</strong> etwas beim Start <strong>de</strong>r<br />
Anwendung übergeben könnten. Der Datentyp ist string und es han<strong>de</strong>lt sich um<br />
92
ein Array, was wir an <strong>de</strong>n eckigen Klammern erkennen. Der Variablenname<br />
args ist in diesem Fall willkürlich gewählt und be<strong>de</strong>utet soviel wie Argumente.<br />
Sicherlich haben Sie schon einmal das Programm copy bzw. xcopy innerhalb<br />
<strong>de</strong>r DOS-Box verwen<strong>de</strong>t. Diese nützlichen Tools kopieren Dateien bzw.<br />
Verzeichnisse von einem Speicherort zum an<strong>de</strong>ren. Damit sie ihre Arbeit<br />
erfolgreich ausführen können, sind mehrere zusätzliche Angaben erfor<strong>de</strong>rlich.<br />
Was möchten Sie wohin kopieren. Nehmen wir z.B. copy. Sie müssen als erstes<br />
Argument angeben, was sie kopieren möchten und als zweites wohin. Also<br />
vielleicht:<br />
copy c:\test.txt d:\info<br />
Diese Befehlszeile kopiert die Datei test.txt <strong>de</strong>r Partition c:\ in das Verzeichnis<br />
info <strong>de</strong>r Partition d:\.<br />
Dotty: „Wie können wir aber aus <strong>de</strong>r Entwicklungsumgebung heraus <strong>de</strong>r<br />
Anwendung einige Argumente übergeben?“<br />
Ganz einfach! Im Projektmappen-Explorer klicken wir auf unser Projekt und<br />
öffnen über die rechte Maustaste das Kontext-Menü und wählen dort <strong>de</strong>n<br />
Eintrag Eigenschaften. Danach wechseln wir in <strong>de</strong>n Reiter Debuggen.<br />
93
Sie erkennen auf <strong>de</strong>r rechten Seite das Textfeld Befehlszeilenargumente. Dort<br />
geben Sie die einzelnen Argumente durch Leerzeichen getrennt an. Wen<strong>de</strong>n wir<br />
uns jedoch jetzt wie<strong>de</strong>r <strong>de</strong>r Anwendung zu. Vorab habe ich folgen<strong>de</strong> Einträge<br />
vorgenommen:<br />
Sie sehen, dass es sich um 3 Argumente han<strong>de</strong>lt.<br />
94
Die Funktion <strong>de</strong>r foreach-Schleife sollte Ihnen bekannt sein. Sie durchläuft in<br />
diesem Fall alle Elemente <strong>de</strong>s Arrays args, wobei bei je<strong>de</strong>m einzelnen<br />
Durchlauf das ermittelte Element in <strong>de</strong>r Variablen strArgument abgelegt wird.<br />
In Zeile 15 erfolgt die Ausgabe auf die Konsole, die da lautet:<br />
<strong>C#</strong><br />
macht<br />
Spass!<br />
Sie können natürlich auch die alternative Möglichkeit mit einer for-Schleife<br />
programmieren.<br />
95
Zeile<br />
Erklärung<br />
14 Überprüfung, ob überhaupt Argumente übergeben wur<strong>de</strong>n. Falls nicht, wird eine<br />
Meldung auf <strong>de</strong>r Konsole ausgegeben.<br />
17, 18 Wur<strong>de</strong>n Argumente übergeben (args.Length != 0), wer<strong>de</strong>n diese über eine for-Schleife<br />
Tab. 13:<br />
an die Konsole geschickt.<br />
Co<strong>de</strong>-Erläuterungen<br />
Natürlich kann die Metho<strong>de</strong> Main() auch einen Wert zurückliefern. Ersetzen Sie<br />
einfach das Schlüsselwort void durch int.<br />
Enumerationen<br />
Sie haben kürzlich die Konstanten kennen gelernt. Es han<strong>de</strong>lte sich dabei um<br />
ein Sprachelement, welches einen Wert speichern kann und zur Laufzeit nicht<br />
verän<strong>de</strong>rt wer<strong>de</strong>n darf. Enumerationen o<strong>de</strong>r auch Aufzählungen sind ähnliche<br />
Konstrukte. Eine Enumeration gruppiert mehrere Konstante, die zu einem<br />
bestimmten Kontext gehören. Möchten Sie in Ihrem Programm z.B. die<br />
Planeten unseres Sonnensystems ansprechen, so ist es möglicherweise etwas<br />
umständlich und unübersichtlich, sie anhand ihrer Positionsnummer zu<br />
benennen. Schauen Sie sich dazu das folgen<strong>de</strong> Enumerations-Beispiel unseres<br />
Sonnensystems an und nageln Sie mich bitte nicht darauf fest, dass ich nicht<br />
alle neu hinzugekommenen Planeten, die erst kürzlich <strong>de</strong>n Aufnahmetest<br />
bestan<strong>de</strong>n haben, auch noch aufführe. Pluto scheint ja nicht mehr dazu zu<br />
gehören.<br />
96
Zeile<br />
Erklärung<br />
9 - 20 Definition <strong>de</strong>r Enumeration Sonnensystem vom Datentyp int<br />
25 Deklaration <strong>de</strong>r Variablen MeinSystem vom Typ Sonnensystem<br />
27 Initialisierung <strong>de</strong>r Variablen MeinSystem mit einem Wert aus <strong>de</strong>r Auswahlliste<br />
Tab. 14: Co<strong>de</strong>-Erläuterungen<br />
97
Die allgemeine Syntax für eine Enumeration lautet:<br />
enum Name [:] { };<br />
Wie Sie sehen, ist <strong>de</strong>r angegebene Basisdatentyp optional. Lassen Sie ihn weg,<br />
so wird standardmäßig <strong>de</strong>r Datentyp auf int gesetzt. Es können nur die<br />
ganzzahligen Datentypen byte, short, int und long verwen<strong>de</strong>t wer<strong>de</strong>n. Sie sehen<br />
in Zeile 11, dass ich <strong>de</strong>r Konstanten Merkur explizit einen Wert zugewiesen<br />
habe. Alle nachfolgen<strong>de</strong>n Konstanten, also Venus, Er<strong>de</strong>, Mars usw., benötigen<br />
eigentlich keine explizite Angabe eines Wertes, wenn dieser sich bei je<strong>de</strong>r<br />
neuen Konstanten um <strong>de</strong>n Wert 1 erhöhen soll. Dies geschieht implizit. Wir<br />
hätten also auch<br />
schreiben können. Wird bei <strong>de</strong>m ersten Element <strong>de</strong>r Aufzählung kein<br />
Initialisierungswert angeben, erhält dieses implizit <strong>de</strong>n Wert 0 zugewiesen. Es<br />
ist jedoch auch möglich, die Folge an einer beliebigen Stelle mit einem an<strong>de</strong>ren<br />
Wert fortsetzen zu lassen. Alle nachfolgen<strong>de</strong>n Elemente wer<strong>de</strong>n bezogen auf<br />
<strong>de</strong>n neuen Startwert, wie<strong>de</strong>rum jeweils um <strong>de</strong>n Wert 1 erhöht.<br />
98
Die Konstanten besitzen nach dieser Deklaration folgen<strong>de</strong> Werte:<br />
Konstante Wert Initialisierung<br />
Melmac 17 Explizit<br />
Eligor 18 Implizit<br />
Mindrap 19 Implizit<br />
Luginar 73 Explizit<br />
Lemgos 74 Implizit<br />
Eltar 75 Implizit<br />
Die Enumeration hat im IntelliSense ein eigenes Icon:<br />
Icon<br />
Erklärung<br />
Tab. 15:<br />
Signalisiert, dass es sich um eine Enumeration han<strong>de</strong>lt<br />
Signalisiert, dass es sich um ein Element (Konstante) <strong>de</strong>r Enumeration han<strong>de</strong>lt<br />
Iconerklärungen<br />
Innerhalb von .NET sind etliche Enumerationen vorhan<strong>de</strong>n, die Sie auf Schritt<br />
und Tritt verfolgen. Das folgen<strong>de</strong> Beispiel legt eine Hintergrundfarbe für die<br />
Konsole fest, wobei die einzelnen Farben in einer Enumeration abgelegt sind.<br />
99
Dotty: „Gibt es eine Möglichkeit, alle Elemente einer Enumeration mit ihren<br />
Werten auszugeben?“<br />
Elemente <strong>de</strong>r Enumeration ausgeben<br />
Sicherlich! Das folgen<strong>de</strong> Co<strong>de</strong>fragment schreibt alle Konstanten <strong>de</strong>r<br />
Enumeration Sonnensystem mit ihren Werten auf die Konsole:<br />
100
Die Konsolen-Ausgabe lautet:<br />
Element: Melmac Value: 17<br />
Element: Eligor Value: 18<br />
Element: Mindrap Value: 19<br />
Element: Luginar Value: 73<br />
Element: Lemgos Value: 74<br />
Element: Eltar Value: 75<br />
Dazu nutzen wir zwei Metho<strong>de</strong>n <strong>de</strong>r Struktur Enum, von <strong>de</strong>r die<br />
Aufzählungstypen abgeleitet wer<strong>de</strong>n.<br />
101
Metho<strong>de</strong><br />
Erklärung<br />
GetValues() Liest die Werte <strong>de</strong>r Konstanten einer<br />
Enumeration aus. Rückgabe: Array<br />
GetName()<br />
Liefert bei angegebenem Wert <strong>de</strong>n Namen <strong>de</strong>r<br />
Konstanten<br />
Tab. 16: Metho<strong>de</strong>n <strong>de</strong>r Enum-Struktur<br />
Werte <strong>de</strong>r Konstanten ausgeben<br />
Gehen wir die Sache mal im Detail durch. Die Metho<strong>de</strong> GetValues() liefert<br />
beim Aufruf ein Array mit <strong>de</strong>n einzelnen Werten <strong>de</strong>r <strong>de</strong>finierten Konstanten<br />
zurück.<br />
Die Konsolen-Ausgabe lautet:<br />
Value: 17<br />
Value: 18<br />
Value: 19<br />
Value: 73<br />
Value: 74<br />
Value: 75<br />
Sie sehen, dass alle in <strong>de</strong>r Enumeration <strong>de</strong>finierten Werte ausgegeben wer<strong>de</strong>n.<br />
Dotty: „Was be<strong>de</strong>utet das typeof(Sonnensystem)?“<br />
102
Die Metho<strong>de</strong> typeof() liefert eine Typbeschreibung eines Datentyps zurück.<br />
Diese Typbeschreibung wird in <strong>de</strong>r Metho<strong>de</strong> GetValues() als Argument<br />
übergeben.<br />
Der Tooltip von GetValues() liefert einige wichtige Hinweise über <strong>de</strong>n Typ<br />
<strong>de</strong>s Parameters und <strong>de</strong>n Rückgabetyp.<br />
Haben wir die Werte ermittelt, können wir mit Hilfe <strong>de</strong>r Metho<strong>de</strong> GetName()<br />
<strong>de</strong>n Namen <strong>de</strong>r Konstanten erfahren, die sich dahinter verbirgt. Das folgen<strong>de</strong><br />
kleine Programm fragt über die Konsole nach einem Wert und gibt<br />
anschließend <strong>de</strong>n passen<strong>de</strong>n Konstantennamen aus.<br />
Die Metho<strong>de</strong> GetName() erwartet dabei zwei Parameter. Der erste ist <strong>de</strong>r Typ<br />
<strong>de</strong>r Enumeration und <strong>de</strong>r zweite <strong>de</strong>r Konstantenwert, <strong>de</strong>r zur Ermittlung <strong>de</strong>s<br />
Namens benötigt wird.<br />
Konstantennamen ausgeben<br />
Es existiert übrigens noch eine interessante Metho<strong>de</strong> zur Ermittlung <strong>de</strong>r<br />
Konstantennamen. Wir sind bisher über die vorher ermittelten Werte zu <strong>de</strong>n<br />
103
Namen gelangt. Die Metho<strong>de</strong> GetNames() liefert ein Array mit allen<br />
Elementen <strong>de</strong>r Enumeration zurück.<br />
Die Konsolen-Ausgabe lautet:<br />
Melmac<br />
Eligor<br />
Mindrap<br />
Luginar<br />
Lemgos<br />
Eltar<br />
Dotty: “Gibt es eine Möglichkeit herauszufin<strong>de</strong>n, ob ein bestimmter Name in<br />
einer Enumeration <strong>de</strong>finiert wur<strong>de</strong>?”<br />
Das ist machbar. Die Struktur Enum bietet dazu eine Metho<strong>de</strong> mit Namen<br />
IsDefined() an. Beachten Sie, dass bei <strong>de</strong>r Übergabe <strong>de</strong>s Namens an die<br />
Metho<strong>de</strong> die Schreibweise (Case-Sensitive) eine Rolle spielt.<br />
104
Der vorliegen<strong>de</strong> Co<strong>de</strong> überprüft das Vorhan<strong>de</strong>nsein <strong>de</strong>s Elements Lemgos in <strong>de</strong>r<br />
Enumeration Sonnensystem.<br />
Basisdatentyp zur Laufzeit ermitteln<br />
Dotty: „Können wir mit Hilfe einer Metho<strong>de</strong> <strong>de</strong>n Basisdatentyp einer<br />
Enumeration zur Laufzeit ermitteln?“<br />
Das ist mit <strong>de</strong>r Metho<strong>de</strong> GetUn<strong>de</strong>rlyingType() möglich.<br />
Die Konsolen-Ausgabe für unser Beispiel lautet:<br />
System.Int32<br />
Das ist <strong>de</strong>r .NET-Laufzeittyp für <strong>de</strong>n Datentyp int, <strong>de</strong>r implizit für unsere<br />
Enumeration vergeben wur<strong>de</strong>. Doch schauen wir uns zum Schluss noch ein<br />
kurzes Anwendungsbeispiel an, wobei eine Metho<strong>de</strong> genutzt wird, <strong>de</strong>r wir ein<br />
Argument übergeben. Das erwartete Argument ist eine Enumeration, so dass<br />
sich die Auswahl über IntelliSense als sehr hilfreich und viel sagend erweist. Es<br />
ist auf je<strong>de</strong>n Fall für einen Menschen einleuchten<strong>de</strong>r bzw. verständlicher, eine<br />
für sich sprechen<strong>de</strong> Enumeration anzugeben, als irgen<strong>de</strong>inen nichts sagen<strong>de</strong>n<br />
Zahlenwert. Für das folgen<strong>de</strong> Beispiel lege ich wie<strong>de</strong>r die eingangs vorgestellte<br />
Enumeration über unser Sonnensystem zugrun<strong>de</strong>. Die Metho<strong>de</strong> gestaltet sich<br />
folgen<strong>de</strong>rmaßen:<br />
105
Sie sehen, dass <strong>de</strong>r Parameter s vom Datentyp Sonnensystem ist. Dieser wird in<br />
einer case-Anweisung auf <strong>de</strong>n Inhalt überprüft und entsprechend verzweigt. Der<br />
Einfachheit halber habe ich nur zwei Möglichkeiten zur Auswahl angeboten,<br />
doch Sie sehen sicherlich sofort, worum es geht. Der Aufruf <strong>de</strong>r Metho<strong>de</strong> lautet<br />
dann:<br />
Ist doch fantastisch! IntelliSense bietet uns die zur Verfügung stehen<strong>de</strong>n<br />
Konstanten zur Auswahl an. Da gehören Tippfehler <strong>de</strong>r Vergangenheit an und<br />
Sie können sich sofort etwas unter <strong>de</strong>n für sich sprechen<strong>de</strong>n Konstanten<br />
vorstellen.<br />
106
Dotty: „Die Auswahl <strong>de</strong>r Konstanten in <strong>de</strong>r Enumeration entspricht in <strong>de</strong>r<br />
Reihenfolge aber nicht <strong>de</strong>r in <strong>de</strong>r Deklaration. Wie kommt das?“<br />
Verantwortlich dafür ist die Entwicklungsumgebung, die die Einträge<br />
alphabetisch sortiert. Das soll uns aber nicht weiter beunruhigen.<br />
107