Sie sind auf Seite 1von 214

C# Grundlagen

1. Vorwort 2. Einleitung 3. Entwicklungsumgebung

4.

Grundlagen (Ver. 1.03)

5. Debugging 6. Objektorientierung 7. Fehlerbehandlung

by Erik Bartmann

Grundlagen...................................................................................................6 Der erste Kontakt .............................................................................................6 Mein erstes Programm .....................................................................................7 Strukturierung ............................................................................................13 Womit gehts denn los? .............................................................................14 Grundlagen von C# und IDE .........................................................................15 Anweisung(en)...........................................................................................15 Anweisungsblcke sichtbar machen ..........................................................16 Gltigkeitsbereiche ....................................................................................17 Gliederungserweiterung / -verminderung ..................................................18 Namespaces................................................................................................22 Einrckungen (Das Auge isst mit) .............................................................22 Kommentare...............................................................................................24 Aufgabenliste erstellen...............................................................................32 Gro- und Kleinschreibung........................................................................36 Variablen........................................................................................................37 Wozu Variablen? .......................................................................................37 C# Schlsselwrter ....................................................................................38 Deklaration.................................................................................................40 Schreibweisen ............................................................................................42 Initialisierung .............................................................................................43 Variablennamen und Prfixe......................................................................51 Ausgabe eines Variableninhaltes ...............................................................53 Einlesen in eine Variable ...........................................................................58

Datentypen und Wertebereiche.................................................................. 60 Literale unter die Lupe genommen ............................................................ 66 Was fr ein Typ bist Du?........................................................................... 72 Typumwandlungen .................................................................................... 74 Konvertierungsmethoden........................................................................... 86 Arrays ............................................................................................................ 89 Eindimensionale Arrays............................................................................. 90 Arraygrenzen zur Laufzeit bestimmen....................................................... 93 Mehrdimensionale Arrays.......................................................................... 96 Ungleichfrmige Arrays .......................................................................... 101 Anzahl der Array-Elemente bestimmen................................................... 103 Anzahl der Dimensionen ermitteln .......................................................... 104 Arrayelemente lschen ............................................................................ 105 Array klonen ............................................................................................ 106 Array kopieren ......................................................................................... 107 Reihenfolge eines Arrayinhaltes umkehren ............................................. 111 Array sortieren ......................................................................................... 111 Operatoren ................................................................................................... 113 Arithmetische Operatoren........................................................................ 115 Inkrement- und Dekrement-Operatoren................................................... 118 Vergleichsoperatoren (Relationale Operatoren) ...................................... 120 Logische Operatoren................................................................................ 122 Bitweise Operatoren ................................................................................ 127 Zuweisungsoperatoren ............................................................................. 136 Sonstige Operatoren................................................................................. 140 3

Rangfolge.................................................................................................142 Kontrollstrukturen........................................................................................143 if-Anweisung............................................................................................144 if-else-Anweisung ....................................................................................147 if-else-if Konstrukt...................................................................................149 ?:-Operator ...............................................................................................150 switch-Anweisung....................................................................................152 goto-Anweisung (auerhalb von switch) .................................................159 Schleifen ......................................................................................................161 for-Schleife ..............................................................................................161 foreach-Schleife .......................................................................................171 while-Schleife ..........................................................................................172 do-Schleife ...............................................................................................178 Verschachtelung.......................................................................................181 Zeichenketten...............................................................................................183 Verbatim Strings ......................................................................................185 Initialisierung ...........................................................................................186 Leerstring zuweisen .................................................................................189 Lnge einer Zeichenkette .........................................................................190 Einzelne Zeichen extrahieren...................................................................192 Zeichenketten verbinden bzw. verketten .................................................194 Innerhalb von Zeichenketten suchen........................................................197 Umwandlung in Grobuchstaben.............................................................200 Umwandlung in Kleinbuchstaben............................................................201 Zeichenketten zerlegen ............................................................................201 4

Zeichenketten vergleichen #1 .................................................................. 204 Zeichenketten vergleichen #2 .................................................................. 205 Teilstring Einfgen .................................................................................. 209 Teilstring Ersetzen ................................................................................... 210 Teilstring Lschen ................................................................................... 211 ASCII-Code ermitteln.............................................................................. 212 ASCII-Zeichen ermitteln ......................................................................... 213

1 Grundlagen

Der erste Kontakt


Was kann jemanden dazu bewegen, sich an einen Computer zu setzten und die Welt um sich herum zu vergessen? Nun, bei mir war es die Neugier und das Interesse an etwas, was mich schon seid Kindheit an fasziniert hat. Mein erster Computer war ein Commodore CBM 3032 und mein Vater hat ihn mir geschenkt, nur damit ich meine Lust aufs Motorradfahren nicht ausleben sollte. Er hat damit Erfolg gehabt und mich zu etwas verleitet, von dem ich heute nicht mehr loskomme und ich bin glcklich, dass es so geschehen ist. Zwar kann man mit Computern nicht selten einen Crash erleiden, doch ist es im Gegensatz zum Motorradfahren nicht in dem Mae gesundheitsschdlich. Meine ersten Gehversuch, der Eingabe von Befehlen ber die Tastatur, endeten mit klglichen Syntax Error Fehlermeldungen, doch ich war begeistert, dass der Rechner mir antwortete und ich mit einer Maschine in einer Form kommunizierte, die mir bislang vllig unbekannt war. Nach und nach stellten sich dann die ersten Erfolgserlebnisse ein und mein sehnlichster Wunsch bestand von nun an, die Kiste genau das machen zu lassen, was ich mir vorstellte. Und das ist genau der Knackpunkt. Die Voraussetzung fr einen guten Programmierer ist die unbndige Lust am Probieren. Programmieren kann als Kunst oder Wissenschaft angesehen werden und Sie sollten herauszufinden, was machbar ist und es dann umsetzten. Doch es ist nicht nur Zuckerschlecken. Es wird die Zeit kommen, da mchten Sie Ihren Rechner am liebsten in Stcke hauen und ihn in seine Bits und Bytes zerlegen.

Mein erstes Programm


Im Abschnitt ber die Entwicklungsumgebung von Visual Studio Express Edition haben wir die Erstellung eines Konsolen-Projektes kennen gelernt. Auf dieser Erfahrung wollen wir jetzt aufbauen. Schauen Sie sich erneut das von der IDE automatisch generierte Codegerst an.

Fhren wir das Programm aus, erscheint fr einen kurzen Augenblick eine DOS-Box, um augenblicklich wieder zu verschwinden. Die Frage, die sich einige vielleicht stellen: Ist ja doch fr den Anfang einiges an Code. Der soll nichts machen?. Natrlich macht er etwas! Wir haben es doch gesehen. Es wird eine Konsolenanwendung geffnet und wieder geschlossen. Das ist schon eine Leistung. Programm starten Dotty: Wie fhre ich denn mein Programm aus? Es gibt drei Mglichkeiten, das Programm auszufhren: In der Symbolleiste die Schaltflche mit einem kleinen grnen Dreieck drcken. Die Funktionstaste F5 auf Ihrer Tastatur drcken Im Programm-Men Debuggen|Debuggen starten auswhlen

Jetzt fgen wir aber selbst erstellen Programmcode in unsere Anwendung ein. Es gibt jedoch eine nicht geringe Anzahl an Stellen im schon vorhandenen Code, in der der eigene eingefgt werden kann. Ihnen fehlt bis jetzt ein entscheidender Hinweis und wir mssen uns die berechtige Frage stellen: An welcher Stelle im Code beginnt die Ausfhrung? Wenn wir genauer hinschauen, sehen wir vielleicht einen Hinweis, der uns bei der Lsung des Problems behilflich ist. Mitten im Programmcode befindet sich in Zeile 9 das Wort Main. bersetzt bedeutet es: hauptschlich oder wichtigst. Wenn Sie den gleichen Gedanken hatten, dann liegen Sie genau richtig. Main steht in einer Zeile, die ein Unterprogramm in C# darstellt bzw. bezeichnet. Dieses Unterprogramm wird in der Objektorientierten Programmierung als Methode bezeichnet. Das ist zwar ein Vorgriff auf das, was noch kommt, doch an dieser Stelle recht wichtig. Hier nun die beiden angekndigten zustzlichen Zeilen:

Starten Sie jetzt das Programm, dann ffnet sich erneut eine DOS-Box. Doch diesmal sind Informationen zu erkennen, da das Fenster nicht gleich wieder geschlossen wird. Wir sehen den Text Programmieren macht Spa! Was ist passiert? Wir haben unserem Programm die Anweisung gegeben, etwas auf die Konsole auszugeben. Dafr verantwortlich ist in Zeile 11 die Methode WriteLine(). Stren Sie sich im Moment nicht an der Syntax und ignorieren fr den Anfang das System.Console. Was WriteLine() auf die Konsole ausgeben soll, wird zwischen die runden Klammern gesetzt. Da es sich bei den auszugebenden Daten um eine Zeichenkette handelt, muss diese, in doppelte Hochkommata eingeschlossen, als Argument bergeben werden. Zeile 12, die von uns ebenfalls hinzugefgt wurde, sorgt dafr, dass das Fenster nach Abarbeitung des letzten Befehls nicht sofort geschlossen wird. Die Methode ReadLine() erwartet eine Reaktion vom Benutzer. Die Konsole bleibt so lange sichtbar, bis die Enter - Taste gedrckt wird. Sicherlich ist Ihnen auch aufgefallen, dass am Schluss jeder Anweisung ein Semikolon steht. Dadurch wird das Ende einer Befehlszeile gekennzeichnet. Ein in den Anfngen hufig gemachter Fehler ist das Vergessen des Semikolons am Ende einer Anweisung. Provozieren wir nun diesen Fehler und entfernen das Semikolon am Ende der Zeile 11. Die Reaktion der Entwicklungsumgebung erfolgt unmittelbar.

Fehlerliste

Abb. 1:

Fehlerliste

In der schon angesprochenen Fehlerliste signalisiert die Entwicklungsumgebung, dass etwas in unserem Code nicht stimmt. Die Anzahl der Fehler werden im oberen Bereich des Fensters angezeigt, wobei jeder Fehler eine eigene Zeile einnimmt. Im Moment existiert nur ein einziger und in der Spalte Beschreibung wird uns der Grund mitgeteilt. Es wird ein Semikolon erwartet. Jetzt erkennen wir auch den tieferen Sinn von Zeilennummern. Die Spalten Zeile und Spalte geben uns in Form von Quasi-Koordinaten das Ziel an, auf das wir unser Augenmerk lenken mssen.

Dotty: Wie gelange ich am schnellsten an die Stelle im meinem SourceCode, wo der Fehler entdeckt wurde? Muss ich mich durch meinen mglicherweise mehrere hundert Zeilen umfassenden Code scrollen?

10

Natrlich nicht! Es gengt, mit der Maus einen Doppelklick auf die entsprechende Zeile in der Fehlerliste zu machen. Der Cursor springt sofort an die betreffende Stelle im Code. Doch lenken wir erneut unser Augenmerk in das Code-Fenster. Dort sehen wir eine optische Vernderung. An einer Stelle sind rote Schlangenlinien zu erkennen. Fahren wir mit unserem Mauszeiger darber, ffnet sich ein so genannter Tooltip. Das sind kleine Fenster, die Zusatzinformationen liefern, wie Sie es bestimmt schon bei zahlreichen Schaltflchen bemerkt haben, als die Maus eine kurze Zeit ber deren Position verweilte.

Es ist die gleiche Information, die uns schon die Fehlerliste zeigte. Ist doch eine feine Sache und an Komfort kaum zu berbieten, denke ich. Da machen sogar Fehler Spa! Wird das fehlende Semikolon hinzugefgt, aktualisiert sich die Fehlerliste und entfernt den Eintrag automatisch. Dotty: Woher kommen eigentlich die Codezeilen des Programmgersts einer Konsolenanwendung? Eine berechtige Frage! Diese Zeilen werden aus einer projektabhngigen Template-Datei gelesen und knnen sogar nach Wunsch angepasst werden. Doch bevor ich Ihnen zeige, wo sich diese Datei befindet, muss ich Sie darauf hinweisen, dass es sich hierbei um eine Datei handelt, auf die die Entwicklungsumgebung beim Erzeugen des Konsolen-Projektes zugreift. Lschen Sie die Datei unbeabsichtigt oder modifizieren Sie deren Inhalt ohne genau zu wissen, was Sie tun, wird es fatale Folgen fr Ihre Visual Studio Express Edition haben. Halten Sie deswegen bitte folgende Schritte ein:

11

Projekt-Template anpassen 1. Windows-Explorer ffnen 2. Wechseln Sie in das Verzeichnis (ggf. Laufwerksbuchstaben anpassen) C:\Programme\Microsoft Visual Studio 8\ Common7\IDE\VCSExpress\ProjectTemplatesCache\ 1031\ConsoleApplication.zip 3. Erstellen Sie Kopie der Datei program.cs (mit rechter Maustaste anklicken und in einen freien Bereich ziehen. Dann Maustaste loslassen und aus dem Kontext-Men Hierher kopieren whlen 4. Datei program.cs mit einem Text-Editor (z.B. Notepad) editieren

Die Datei stellt sich nach dem ffnen folgendermaen dar:

Sie werden vielleicht die Namespace-Zeile merkwrdig finden. Der Name $safeprojectname$ ist ein Project Template-Parameter und stellt einen Platzhalter dar, der durch den zu Beginn von Ihnen vergebenen Projektnamen ersetzt wird.

12

Mchten Sie z.B. Ihren Source-Code mit dem User-Namen und Erstellungsdatum bzw. -zeit versehen, so erweitern Sie das Template zu Beginn um die folgenden Zeilen: // Autor: $username$ // Time.: $time$ Jedes Mal, wenn Sie ein neues Konsolenprojekt erzeugen, werden Ihr UserName und die aktuelle Systemzeit als Kommentare an den Anfang des Codes gesetzt. Folgend einige Parameter mit deren Erklrungen: Template-Parameter

Parameter machinename registeredorganization

Erklrung Der aktuelle Computername Der Registry-Key Value von HKLM\Software\Microsoft\Windows NT\CurrentVersion\RegisteredOrganization

userdomain username time year

Die aktuelle User-Domain Der aktuelle Username Die aktuelle Zeit im Format MM/DDYYYY 00:00:00 Das aktuelle Jahr im Format YYYY

Tab. 1: Template-Parameter

Strukturierung
Schreiben Sie eine Anwendung in C#, so mssen Sie sich immer daran erinnern, dass jede einzelne Anweisung mit einem Semikolon beendet wird. Auerdem arbeitet C# blockorientiert, was bedeutet, dass ein Codeblock eine oder mehrere Anweisungen zu einer Einheit zusammenfasst. Dazu wird die ffnende bzw. schlieende geschweifte Klammer verwendet. 13

Wir haben es schon bei der Methode Main() kennen gelernt. Sie umschliet die beiden Anweisungen und fhrt sie als eine Einheit gesehen aus.

Womit gehts denn los?


Die Frage, womit bzw. an welcher Stelle es im Programm-Code losgeht, wurde schon angesprochen. Jede Anwendung muss eine Methode namens Main() besitzen.

14

Sie stellt den Einsprungpunkt dar, um einen definierten Start jedes Programms zu gewhrleisten. Alle von ihr durch das Klammerpaar eingeschlossen Anweisungen, werden zur Ausfhrung gebracht. Dotty: Was bedeuten denn die restlichen Bestandteile? An dieser Stelle ist es noch etwas verfrht, um genauer darauf einzugehen. Das Kapitel ber die Objektorientierte Programmierung liefert die Antworten. Ich mchte nur so viel sagen, dass uns in C# Objekte auf Schritt und Tritt verfolgen. Diese Objekte mssen zur Laufzeit des Programms erst erschaffen werden. Da jedoch bei Beginn eines Programms noch keine existieren, muss es eine Mglichkeit geben, einen Einstieg zu finden. Die Methode Main wird innerhalb der Klasse Program als static deklariert, was bedeutet, dass kein konkretes Objekt fr die Ausfhrung bentigt wird. Damit ist der Anfang gemacht.

Grundlagen von C# und IDE

Anweisung(en)
Damit uns der Computer versteht, mssen wir uns ihm in irgendeiner Art und Weise verstndlich machen. Deshalb wurde eine Kommunikationsmglichkeit geschaffen, welche sich auf einzelne Anweisungen reduzieren lsst, die sequentiell, d.h. der Reihe nach ausgefhrt werden. Anweisungen werden durch ein Semikolon voneinander getrennt angegeben. 15

Das folgende Code-Beispiel zeigt uns einen Anweisungsblock, der mehrere Anweisungen zusammenfasst, als wre es eine einzige. Dabei spielen die geschweiften Klammerpaare eine entscheidende Rolle, die die einzelnen Anweisungen zu einem Block zusammenfassen.

Anweisungsblcke sichtbar machen


Erstreckt sich die Blockbildung ber mehrere Zeilen und sind sie zudem noch geschachtelt, leidet die bersichtlichkeit ein wenig, so dass nicht auf den ersten Blick zu erkennen ist, welche die ffnende und die korrespondierende schlieende Klammer ist. Die IDE hilft uns bei der Antwort. Klicken Sie mit dem Mauszeiger entweder vor eine ffnende oder hinter eine schlieende geschweifte Klammer. Sofort wird das Klammerpaar optisch hervorgehoben, so dass der Anweisungsblock unmittelbar sichtbar wird.

Ich habe vor die ffnende Klammer der Klasse Program mit meinem Mauszeiger geklickt und das Klammerpaar wird augenblicklich farblich hinterlegt. Natrlich ist es das vorliegende Beispiel zu diesem Zeitpunkt absolut simpel, doch Sie sehen auf Anhieb, was ich meine. Gerade bei verschachtelten Anweisungsblcken, die z.B. bei mehreren ineinander greifenden Schleifen zum Einsatz kommen, ist es nicht immer einfach, den berblick zu wahren. Was mit 16

geschweiften Klammerpaaren Klammerpaaren mglich.

funktioniert,

ist

ebenso

mit

runden

Gltigkeitsbereiche
Sie haben eben Anweisungsblcke, die durch das geschweifte Klammerpaar definiert werden, kennen gelernt. Ein Gltigkeitsbereich wird eben durch diese Klammern gekennzeichnet bzw. festgelegt. Folgende Anweisungen sind nicht zulssig, da sich zwei Variablen gleichen Namens im selben Gltigkeitsbereich befinden. Was macht das auch fr einen Sinn?

Die Entwicklungsumgebung quittiert ein solches Vorgehen mit einem Fehler. Sie erkennen das z.B. an den Schlangenlinien unterhalb des zweiten as. Die Fehlerbeschreibung ist in diesem Fall recht aussagekrftig.

Das folgende Beispiel beweist zudem, dass eine Variable in einem Anweisungsblock in selbigem und den darunter liegenden gltig ist.

Die Fehlermeldung lautet in diesem Fall:

17

Gliederungserweiterung / -verminderung
Dotty: Was sind das eigentlich fr merkwrdige Minus-Zeichen am linken Editorrand und welche Funktionalitt verbirgt sich dahinter?

Diese Minus-Zeichen stehen fr eine Gliederungsverminderung und befinden sich am Anfang eines von der IDE erkannten Strukturblocks. Der Namespace ConsoleApplication1 umschliet die Klassendefinition Program, welche wiederum die Methode Main umschliet, usw. Fhren Sie z.B. mit der linken Maustaste einen Klick auf das Minus-Zeichen in der Zeile 9 durch, so minimiert sich der Strukturblock der Methode Main auf eine einzige Zeile. Natrlich werden die verschwindenden Zeilen nicht gelscht, sondern nur der bersicht wegen, ausgeblendet.

18

brig bleibt die Zeile 9 mit einem grafischen Hinweis in Form eines Rechtecks mit 3 Punkten. Fahren Sie mit der Maus ber diesen Bereich, so ffnet sich ein Tooltip mit den Informationen ber die verborgenen Codezeilen. Das MinusZeichen ist brigens zu einem Plus-Zeichen mutiert und signalisiert uns, dass eine Gliederungserweiterung vorgenommen werden kann, welche die ursprngliche Darstellung wieder herstellt. Haben Sie bestimmte Codebereiche zur Genge getestet und ist deren Anzeige im Moment nicht mehr von Nten, knnen Sie sich auf diese Art und Weise ein wenig Platz verschaffen. Falls Sie es fr sinnvoll erachten, ist es sogar mglich, eigene Gliederungen zu erstellen.

19

1. Betreffende Codezeilen (hier Zeilen 11 und 12)

mit

linker

Maustaste

markieren

2. Kontext-Men mit rechter Maustaste ffnen 3. Umschlieen mit... auswhlen 4. Im nun folgenden Fenster treffen Sie die Auswahl Umschlieen mit #region

20

Nach dieser Aktion werden die zuvor markierten Zeilen von einem einleitenden #region und abschlieenden #endregion umschlossen.

Den automatisch vergebenen Namen MyRegion knnen Sie nach eigenen Wnschen, aussagekrftig anpassen. Mittels, des am linken Editorrand auftauchenden Minus-Zeichen (Zeile 11), kann die Region ggf. minimiert werden.

21

Namespaces
Dotty: Was ist denn eigentlich ein Namespace, der eben nur kurz erwhnt wurde? Ein Namespace (bersetzt: Namensraum) legt einen Gltigkeitsbereich fest, innerhalb dessen sich der Source-Code befindet, um eine globale Eindeutigkeit zu gewhrleisten. In der .NET-Standardklassenbibliothek existieren z.B. unzhlige Klassen, welche in einer hierarchischen Struktur ber Namespaces organisiert sind. Sie haben die Anweisung System.Console.WriteLine(...) gesehen und ich hatte eingangs darum gebeten, System.Console zu ignorieren. Jetzt kommt die Auflsung: Die Methode WriteLine(...) gibt eine Zeichenkette auf der Konsole aus. Das war Ihnen zu diesem Zeitpunkt schon bekannt. Die Neuigkeit ist, dass sie zu einer Klasse gehrt, die sich Console nennt. Doch wozu brauchen wir noch die Angabe System? Sie zeigt uns, dass sich die Klasse Console im Namensraum System der .NET Standardklassenbibliothek befindet. Das ermglicht uns z.B. eine eigene Klasse mit gleichem Namen in einem eigenem Namespace zu definieren. Ohne die Namespaces wrde es dann zu einem Namenskonflikt kommen. Jede einzelne Angabe ist durch einen so genannten Punktoperator . voneinander getrennt.

Einrckungen (Das Auge isst mit)


Schauen Sie sich folgenden Code an und beurteilen Sie ihn nicht nach Funktionalitt, sondern nach dem Aspekt der Lesbarkeit. 22

Syntaktisch gesehen, ist das Programm einwandfrei und der Compiler hat nichts zu bemngeln, doch Sie mssen es sich nicht unntig schwierig machen, indem Sie sich einen derartigen Schreibstil angewhnen. Bse Stimmen wrden meinen, daraus eine Verbindung zu Ihrer Denkweise herzustellen. Natrlich kann und will ich niemandem seine Art zu Programmieren vorschreiben. Achten ein wenig auf die Optik, denn sie frdert die Lesbarkeit und Wartbarkeit. Die Entwicklungsumgebung untersttzt Sie insofern bei der Codierung, dass automatisch whrend des Tippens, Zeilen eingerckt bzw. Tabulatoren eingefgt werden. Es sollte deswegen kein Erscheinungsbild, wie oben gezeigt, entstehen. Falls es dennoch mal aus ungeklrten Umstnden zu einer solchen Entgleisung gekommen ist, mssen Sie nicht alles neu schreiben. Markieren Sie einfach den entsprechenden Codebereich mit gedrckter linker Maustaste und klicken auf den Button Hebt die Auskommentierung der ausgewhlten Zeilen auf, in der Symbolleiste.

Abb. 2:

Automatisch Codezeilen Formatieren

23

Dotty: Was hat die Erklrung des Buttons mit der automatischen Formatierung zu tun? Gute Frage! Doch wir kommen spter, wenn es um Kommentare geht, zu diesem Punkt zurck. Es sei gesagt, dass es mglich ist, mehrere Codezeilen in Kommentarzeilen umzuwandeln. Sie werden dann bei der Kompilierung ignoriert und dienen allenfalls als Hinweis oder als Kommentare fr den Programmierer. Und eben diese Aktion ist rckgngig zu machen, wobei die IDE die Codezeilen formatiert, um ein ansprechendes und bersichtliches Erscheinungsbild zu gewhrleisten. Voil.

Kommentare

Jetzt kommen wir zu einem Punkt, der von vielen allzu stiefmtterlich behandelt wird. Ich kann es vollkommen nachvollziehen, wenn ein Programmierer so richtig vom - Fieber gepackt - drauf los schreibt. Dabei wird leider etwas Wichtiges vergessen. Anwendungen sollten mit so genannten Kommentaren versehen werden, die als Gedankensttze dienen und dafr sorgen, den Code auch spter noch zu verstehen. Fr manche Programmierer ist das Kommentieren bzw. Dokumentieren des eigenen Codes eine unzumutbare Last und rangiert in der nach oben offenen Leidensskala knapp unterhalb einer 24

Wurzelbehandlung. Sie werden feststellen mssen, dass nach erschreckend kurzer Zeit der eigene Code befremdlich wirkt. Einige Wochen oder Monaten spter treten garantiert Schwierigkeiten bei der Rekonstruktion Ihrer eigenen Gedankengnge auf. Stellen Sie sich nun vor, Sie mssen die Arbeit Ihres Kollegen, der gerade in Urlaub gefahren oder krank geworden ist, fortfhren. Bevor Sie sich der Lsung des eigentlichen Problems widmen knnen, vergeht wertvolle Zeit beim Entziffern und Verstehen des Fremdcodes. C# kennt drei Varianten von Kommentaren. Einzeilige Kommentare Mehrzeilige Kommentare Dokumentations-Kommentare

Einzeilige Kommentare Einzeilige Kommentare werden durch zwei Slashes // eingeleitet und enden implizit mit dem Zeilenende.

Alles, was hinter den zwei Slashes steht, wird vom Compiler ignoriert. Mchten Sie zu Testzwecken eine oder auch mehrere Codezeilen nicht ausfhren lassen, ist es nicht notwendig diese zu lschen, um sie ggf. nachher mhsam wieder einzufgen. Setzen Sie einfach Kommentarzeichen an den Anfang der Zeile(n). Der Compiler wird sie nicht sehen und so tun, als wrden sie nicht existieren. Mehrzeilige Kommentare Mssen mehrzeilige Kommentare erstellt werden, so gibt es eine zweckmige Alternative. Es existiert eine Kommentarvariante, die auch in der 25

Programmiersprache C Verwendung findet. Zur Kommentareinleitung werden die Zeichen /* und zur Kommentarbeendung die Zeichen */ gesetzt.

Dokumentations Kommentare Ich mchte der Vollstndigkeit halber die Dokumentations Kommentare, auch XML-Dokumentationen genannt, an dieser Stelle ansprechen. Sie werden durch drei aufeinander folgende Slashes /// eingeleitet. Positionieren Sie den Cursor dazu an den Anfang eines Codeblockes, der der Dokumentation bedarf. Hierzu zhlen Klassen, Eigenschaften, Instanzvariablen, Methoden, Enumerationen, um nur einige zu nennen. Probieren wir es direkt an einem Beispiel aus:

1. Positionieren Sie den Cursor an den Zeilenanfang der Main-Methode 2. Drcken Sie die Enter - Taste, um eine Leerzeile einzufgen 3. Drcken Sie die Cursoroben - Taste, um in die neu entstandene Leerzeile zu gelangen 4. Geben Sie drei aufeinander folgende Slashes /// ein. Die IDE erstellt automatisch ein Codegerst in Form eines XML-Kommentars mit XMLTags

26

Bei Fragen zu XML muss ich auf entsprechende Literatur bzw. das Internet verweisen. XML ist die Abkrzung fr EXtensible Markup Language und heit bersetzt: Erweiterbare Auszeichnungssprache. Sie ermglicht das Speichern von Daten in reinem Textformat und beschreibt Strukturen bzw. Daten. Die so genannten Tags werden durch die die ffnende < bzw. schlieende > eckige Klammer gekennzeichnet. <summary> ist im o.g. Code das einleitende und </summary> das schlieende Tag. Zwischen diesen beiden Tags wird eine Beschreibung des entsprechenden Codeabschnitts eingefgt, der sich spter im generierten XML-Dokument wieder findet. Zudem werden die eingetragenen Informationen sowohl von IntelliSense als auch vom Objekt-Browser genutzt. Sehen wir uns dazu ein konkretes Beispiel an. Einige Aspekte der Programmierung sind fr Sie an dieser Stelle vielleicht noch nicht zu verstehen. berspringen Sie dann einfach diesen Teil und kommen spter hierher zurck. Die folgende Methode Add() soll zum besseren Verstndnis dokumentiert werden. Ihre Aufgabe besteht in einer simplen Addition zweier Zahlen.

27

Gehen Sie dafr die folgenden Schritte durch: 1. Positionieren des Cursors an die markierte Stelle 2. Geben Sie drei aufeinander folgende Slashes /// ein. Die IDE erstellt automatisch ein Codegerst in Form eines XML-Kommentars mit XMLTags Das Ergebnis sieht folgendermaen aus:

28

Die Methode Add() besitzt zwei Parameter, die addiert werden und das Ergebnis an den Aufrufer zurck liefert. Es wurde automatisch ein <summery> Tag generiert, wo die allgemeine Funktionsbeschreibung der Methode zu platzieren ist. Zudem sehen Sie zwei Tags mit der Bezeichnung <param> deren Attribute die Namen der in der Methode vorkommenden Parameter besitzen. Dort platzieren Sie die Beschreibung des jeweiligen Parameters. Die vollstndige Dokumentation knnte folgendermaen lauten:

Dotty: Das ist zwar schn und gut, doch wo sehe ich diese Informationen spter? Beim Kodieren zeigt die Entwicklungsumgebung diese Informationen unmittelbar an. Hier sehen Sie die Summary-Informationen im ToolTip.

29

Nach dem Tippen des Methodennamen und der nachfolgenden runden Klammer zeigt die Entwicklungsumgebung die Informationen zum ersten Parameter an.

Sie knnen sich zustzlich alle relevanten Informationen im Objekt-Browser (Strg -W, J) anzeigen lassen.

30

Kommen wir jetzt wieder zu einem schon erwhnten Feature. Mssen mehrere Codezeilen gleichzeitig in Kommentarzeilen gewandelt werden, geht man folgendermaen vor: 1. Codezeilen mit gedrckter linker Maustaste markieren 2. In der Symbolleiste die Schaltflche Kommentiert die ausgewhlten Textzeilen aus drcken

Ihnen ist bestimmt schon aufgefallen, dass die IDE den Source-Code zur besseren bersicht, farblich darstellt. Solche Syntaxhervorhebung frdert die Lesbarkeit, so dass Strukturen leichter zu erkennen sind. Unterluft Ihnen ein Schreibfehler, so wird die erkannte bzw. nicht erkannte Anweisung farblich anders dargestellt. Welche Farbgebung fr welche Struktur angewendet wird, ist nicht in Stein gemeielt. All diese Einstellungen sind an persnliche Bedrfnisse bzw. Wnsche anzupassen. Hinter dem Men Extras|Optionen verbergen sich im Dialogfenster unter 31

Umgebung|Schriftarten Einstellmglichkeiten:

und

Farben

folgende

individuelle

Abb. 3:

Schriftarten und Farben einstellen

Aufgabenliste erstellen
Bereitet Ihnen eine bestimmte Aufgabe Probleme bei der Umsetzung, so dass im Moment zu viel Zeit darauf verwendet wird, knnen Sie sich entscheiden, zuerst an anderer Stelle im Projekt weiter zu machen. Doch wie findet man die Problemstelle im Source-Code wieder? Stellt das fehlende Teilstck ein syntaktisches Problem dar, so wird der Compiler beim Erreichen meckern und an entsprechender Stelle stehen bleiben. Doch was ist, wenn z.B. eine Formel nicht ausgereift ist und nur falsche Ergebnisse liefert? Allzu leicht wird das Nachbessern des fehlerhaften Programmsegmentes bersehen und die Anwendung arbeitet nicht zufrieden stellend. Das Hantieren mittels Zettelwirtschaft kann gelingen, doch was ist, wenn der oder die Zettel nicht mehr aufzufinden sind? Ok, wir schreiben uns die ntigen Informationen in eine 32

Datei und speichern Sie auf unseren Rechner. Keine schlechte Idee, doch immer noch nicht optimal, da an zwei unterschiedlichen Stellen Informationen gepflegt werden mssen. Angesichts der stndigen nderungen im Source-Code, ist z.B. die Synchronisierung in Bezug auf Zeilennummern ein aussichtsloses Unterfangen. C# unter .NET bietet die Mglichkeit im Code Marken zu setzen, die in einer separaten Liste erscheinen, aber Teil des Projektes sind.

Dort knnen Sie so genannte ToDos (also was noch zu erledigen ist) verwalten. Eine solche Zeile fngt wie ein einzeiliger Kommentar an, gefolgt von dem Schlsselwort todo. Auf diese Weise sind wichtige Informationen fr den oder die Programmierer ein Bestandteil des Projektes und gehen nicht verloren oder werden bersehen.

33

In Zeile 11 sehen wir den Kommentar mit dem nachfolgenden Schlsselwort todo. Doch was nun? Sie suchen bestimmt das passende Fenster, um die Informationen anzuzeigen. Unter dem Menpunkt Ansicht findet sich der Eintrag Aufgabenliste.

Abb. 4:

Aufgabeliste anzeigen

Nach der Wahl erscheint am unteren Fensterrand ein neues Fenster mit der Bezeichnung Aufgabeliste 1

34

Abb. 5:

Aufgabenliste

Fr jeden ToDo-Eintrag im Source-Code wird eine separate Zeile innerhalb der Aufgabenliste erstellt. Wir knnen unseren selbst verfassten Hinweis in der Spalte Beschreibung ablesen. Ein Doppelklick mit der linken Maustaste positioniert den Cursor an die entsprechende Stelle im Programmcode. Nach der standardmigen Installation enthlt das System drei unterschiedliche ToDoPrioritten. Niedrig: Normal: Hoch: UNDONE HACK bzw. TODO UnresolvedMergeConflict

Abb. 6:

Tokens fr Aufgabenliste verwalten

Hinter dem Men Extras|Optionen im Punkt Umgebung|Aufgabenliste befindet sich die Liste mit den erstellten Token (Zeichen, Marke). Sie knnen dort neue 35

hinzufgen, nicht mehr bentigte lschen oder vorhandene modifizieren. Abhngig von der gewhlten Prioritt (niedrig oder hoch), erscheint in der ersten Spalte der Fehlerliste ein Icon, das optisch auf die Wichtigkeit der Eintragungen hinweist.

Gro- und Kleinschreibung


C# ist eine Programmiersprache, die eine Differenzierung zwischen Gro- bzw. Kleinschreibung macht. Im Fachjargon wird dies Case-Sensitive genannt. Nehmen wir z.B. die Einsprung-Methode Main. Es macht einen gewaltigen Unterschied, ob Sie Main oder vielleicht main schreiben. Letztere Schreibweise veranlasst den Compiler dazu, einen Fehler anzuzeigen.

Abb. 7:

Fehler bei Gro- bzw. Kleinschreibung

Die von ihm erwartete Methode Main ist aus seiner Sicht einfach nicht vorhanden und somit kann das Programm nicht zur Ausfhrung gebracht werden.

36

Variablen

Wozu Variablen?
Variablen, auch Bezeichner genannt, werden in der Datenverarbeitung genutzt, um Informationen zu speichern und sie spter bei diversen Berechnungen zu verwenden. Vielleicht haben einige von Ihnen schon einmal etwas ber das EVA-Prinzip der Datenverarbeitung gehrt. Die drei Buchstaben sind die Abkrzungen fr Eingabe Verarbeitung Ausgabe Fr ein von uns erstelltes Konsolenprogramm wrde dies bedeuten, dass wir Informationen an die Anwendung bergeben (Eingabe). Der Computer mit Hilfe dieser Daten, welche an einer reservierten Stelle im Speicher abgelegt werden, arbeitet (Verarbeitung) und das Ergebnis anschlieend an die Konsole schickt (Ausgabe). Angenommen, Sie mchten ein Programm schreiben, das Ihnen eine komplizierte Rechenoperation ausfhrt. Es werden anfnglich diverse Anfangsparameter abgefragt, die zur spteren Berechnung natrlich im Computer an irgendeiner Stelle im Speicher temporr zwischengelagert werden mssen. Liegen alle notwendigen Informationen zur Berechnung vor, kann die Anwendung mit der Ausfhrung beginnen. Jetzt greift das Programm auf die von Ihnen eingegeben Daten zurck, damit die Verarbeitung erfolgreich durchgefhrt werden kann. Dotty: Wie findet die Anwendung genau die vom Benutzer gemachten Eingabe wieder? Wie schon eben erwhnt, sind Variablen fr die Aufnahme bzw. Speicherung der Daten verantwortlich. Binr gesehen, wird jeder Wert in Form von Einsen und Nullen im Hautspeicher abgelegt. Der Hauptspeicher ist ein strukturierter Bereich im Computer, der durch die Angabe von Adressen zu verwalten ist. Ein 37

Variablenname stellt bei der Speicherung einen Alias (Ersatzname, Pseudonym) dar, da dieser fr einen Menschen leichter zu behalten ist, als eine Folge von Zeichen und Ziffern.

Abb. 8:

Variable als Referenz

Wie die Abbildung zeigt, fungiert die Variable mit Namen Variable als Referenz auf eine Startadresse im Arbeitsspeicher. Die Adresse ist rein fiktiver Natur und soll nur das Zusammenspiel zwischen Bezeichner und Speicher verdeutlichen. Ein Variablenname kann frei gewhlt werden, unterliegt doch gewissen Einschrnkungen auf die ich in Krze noch zu sprechen komme.

C# Schlsselwrter
Die Programmiersprache C# weist, wie auch andere Sprachen, bestimmte reservierte Schlsselwrter vor. Diese drfen nicht zur Bezeichnung von Variablen, Klassen oder Methoden herangezogen werden. Eine Variable mit Namen if ist nicht zulssig, da der Name schon bei Kontrollstrukturen zum Einsatz kommt.

38

Die Fehlermeldung der IDE ist beraus aussagekrftig und teilt uns mit, dass der Name eines Bezeichners erwartet wird. Der von uns gewhlte Variablenname if ist jedoch ein Schlsselwort. Wenn Sie eine bersicht ber die vorhandenen Schlsselwrter bekommen mchten, wenden Sie sich an die Hilfefunktion der IDE. Gehen Sie ber das Programm-Men Hilfe|Suchen und geben als Suchbegriff Schlsselwrter ein. Bitte beachten Sie, dass damit eine Verbindung zum Internet aufgebaut wird, da die Informationen von einer Microsoft-Seite abgerufen werden. Sehen Sie es dennoch als unbedingt notwenig an, ein Schlsselwort als Bezeichner zu verwenden, so gibt es eine Hintertr, die aber in meinen Augen nicht zum Einsatz kommen sollte, da sie mehr Verwirrung als Nutzen bringt. Stellen Sie dem Schlsselwort den Klammeraffen @ als Prfix voran, um einen gltigen Variablennamen zu erhalten.

Machen Sie sich um die Syntax der Variablennamenvergebung erst mal keine Gedanken. Die so genannte Deklaration wird gesondert angesprochen und lsst nicht mehr lange auf sich warten. Verwenden Sie den o.g. Code, wird eine Variable mit Namen @if angelegt und ihr ein Wert von 10 zugewiesen, der abschlieend an die Konsole ausgegeben wird.

39

Deklaration
Das hat nun wirklich nicht lange gedauert. Wir werden jetzt sehen, warum eine Deklaration (Kundmachung) notwendig ist. In den anfnglichen Zeiten der Basic-Programmierung war es nicht notwendig, vor Gebrauch einer Variablen, diese am Beginn der Anwendung bekannt zu machen. Ihr wurde einfach an der Stelle im Programm ein Wert zugewiesen, an der er bentigt wurde. Zur professionellen Programmierung gehrt die Notwendigkeit, Variablen vor deren Verwendung, dem Compiler bekannt zu geben. Folgende Informationen sind zur erfolgreichen Deklaration erforderlich: Angabe des Datentyps Vergabe des Variablennamen Die Reihenfolge im Code lautet dabei wie folgt: Datentyp Variablenname; Das folgende Beispiel zeigt eine Variablendeklaration mit dem Datentyp Integer, bezeichnend durch den Alias-Namen int. Die unterschiedlichen Datentypen werden ein wenig spter detailliert erklrt, so dass Sie sich jetzt nicht die Haare raufen sollten. Vorab sei gesagt, dass jeder Typ einen definierten Wertebereich besitzt, innerhalb dessen sich eine Zuweisung befinden muss, um nicht einen Laufzeitfehler zu riskieren. Die Folge wre ein unkontrolliertes Beenden der Anwendung, falls keine geeigneten Vorsichtsmanahmen in Form von Ausnahmebehandlungen getroffen wurden. Im Fachjargon werden Ausnahmen Exceptions genannt, denen ich ein eigenes Kapitel widme.

Lassen Sie sich nicht durch das zweite int aus der Ruhe bringen. Es ist Bestandteil des Variablennamen und der ist, wie schon erwhnt, frei zu vergeben. Die geeignete Wahl eines Namen fr einen Bezeichner muss erstens, bestimmten Regel folgen und sollte zweitens, so aussagekrftig wie mglich sein, damit der Sinn bzw. die Aufgabe offensichtlich wird. Das trgt unmittelbar 40

zum Verstndnis des Codes bei (nicht nur fr einen selber, sondern auch fr andere) und leistet auch bei der Fehlersuche unschtzbare Dienste. Doch nun zu den Regeln, die bei der individuellen Vergabe von Namen beachtet werden mssen: Der Name darf sich nur aus alphanumerischen Zeichen inklusive des Unterstriches zusammensetzten. Es drfen keine Sonderzeichen (z.B. %, $, !) bzw. Leerzeichen enthalten sein. Ein Name muss mit einem Buchstaben oder einem Unterstrich beginnen Es darf bei der Vergabe kein Schlsselwort oder der Name einer Methode, Klasse bzw. Objekt verwendet werden Es wird zwischen Gro- bzw. Kleinschreibung unterschieden (CaseSensitive). intWert1 und intwert1 sind unterschiedliche Variablen. Schauen wir uns einige Beispiele zur Variablendeklaration an:

Die Schlangenlinien sind ein Indiz dafr, dass etwas mit der Namensvergabe nicht in Ordnung ist. Erinnern Sie sich in solchen Fllen an die o.g. Regeln und korrigieren entsprechend. Wird versucht, auf eine nicht deklarierte Variable zuzugreifen, weist die Fehlermeldung auf das nicht Vorhandensein im aktuellen Kontext hin.

41

Schreibweisen
Da Sie jetzt wissen, welche Richtlinien es bezglich der Namensvergabe von Bezeichnern gibt, haben wir dennoch die freie Wahl der Kombination von Gro- bzw. Kleinschreibung einzelner Buchstaben. Es existieren dahingehend drei unterschiedliche Anstze, die sich durchgesetzt haben. Camel-Casing Pascal-Casing Uppercase Das Camel-Casing besagt, dass der erste Teil eines zusammengesetzten Wortes klein geschrieben und der erste Buchstabe aller nachfolgenden Teilworte gro geschrieben wird. Wir habe es bei Variablen bzw. Bezeichnern schon angewendet. Das Variablen-Prfix wird klein geschrieben und der nachfolgende eigentliche Variablenname beginnt mit einem Grobuchstaben. strEingabe intVariable1 lngTemp Das Pascal-Casing hingegen legt fest, dass der erste Buchstabe jedes Teilwortes gro geschrieben wird. Es findet Anwendung bei Methoden oder auch Klassennamen. Die Methode Main ist ein Beispiel dafr. Sie besteht zwar nur aus einem einzigen Hauptbegriff, doch der erste Buchstabe ist gro geschrieben. Weitere Beispiele dafr sind: InitApplication SucheNamen ClearScreen

42

Uppercase wird dann verwendet, wenn es um die Deklaration von Konstanten geht. Einschlgige Beispiele hierfr sind die mathematischen Werte: PI E

Abschlieend halten wir fest, dass die Namensvergabe so knapp wie mglich und dennoch aussagekrftig sein sollte.

Initialisierung
Nachdem Sie gesehen haben, wie bei der Deklaration von Variablen der Datentyp und Name festgelegt wird, kommen wir jetzt zur Initialisierung. Das bedeutet, dass der Variablen ein Wert zugewiesen wird, mit dem sich spter arbeiten lsst. Versuchen Sie auf eine Variable zuzugreifen, welche nicht initialisiert wurde, quittiert das die Entwicklungsumgebung gnadenlos mit einem Fehler.

Dotty: Das verstehe ich nicht! Wir haben dem Compiler doch alle notwendigen Informationen mitgeteilt. Datentyp und Variablenname. Das ist korrekt. Doch was sagt uns diese Fehlermeldung zwischen den Zeilen? Wir haben zwar eine Variable deklariert, doch das bedeutet nicht, dass implizit eine Standard-Initialisierung z.B. mit dem Wert 0 vorgenommen wird. Dieser Schritt muss fr lokale Variablen explizit erfolgen. Stren Sie sich nicht an dem Ausdruck lokale Variable. Die Bedeutung erfolgt zu gegebener Zeit. Lassen Sie mich nur kurz erwhnen, dass Variablen, die innerhalb einer Methode (z.B. Main) deklariert wurden, lokale Variablen sind. Sie existieren nur innerhalb dieser Methode und sind auerhalb nicht sichtbar. Wir sprechen hierbei von einem Gltigkeitsbereich. 43

Die Verwendung nicht initialisierter Variablen ist in C# nicht erlaubt. Eine Variable wird mit Hilfe des Zuweisungsoperators = gefolgt von einem Zuweisungswert (Literal), einer anderen Variablen oder eines Ausdruck initialisiert. Ein Literal ist ein, durch seine formale Sprache, festgelegter Name eines Wertes. Es ist ein einfacher Ausdruck, welcher sich nicht weiter zerlegen lsst. Wir werden es in Zukunft mit unterschiedlichen Literalen zu tun bekommen. Bisher sind uns nur die ganzzahligen Literale ber den Weg gelaufen. Eine genauere Erklrung erfolgt etwas spter. Hier eine Auflistung der vorhandenen Literale: Ganzahlige Literale Hexadezimale Literale Gleitkommaliterale Zeichenliterale Escape-Sequenzen Stringliterale Boolesche Literale Nachfolgend sehen Sie ein paar Beispiele fr die Initialisierung von Variablen.

44

In Zeile 12 sehen wir die Deklarierung der Variablen intWert1. Zeile 14 beinhaltet die Initialisierung mit dem Ganzzahl-Literal 90. Die Zeile 18 zeigt die Initialisierung der Variablen intWert2 durch den Inhalt der Variablen intWert1 und Zeile 22 weist der Variablen intWert3 einen Ausdruck zu, welcher vor der Zuweisung berechnet wird. Hexadezimalzahlen Was ein Ganzzahl-Literal ist, haben wir nun kennen gelernt. Sie beschrnkten sich bis jetzt immer nur auf Dezimalzahlen. Es kann u.U. sinnvoll sein, eine Variable mit einem Literal in hexadezimaler Schreibweise zu initialisieren. Das sind Zahlen, die als Basis die Zahl 16 besitzen. Es werden neben den Ziffern 0 bis 9 zustzlich die Buchstaben A bis F mit den Zahlenwerten 10 bis 15 verwendet. Dotty: Da C# Case-Sensitive ist, wird doch bestimmt zwischen den Buchstaben a bis f bzw. A bis F unterschieden, oder? Nein. Keine Regel ohne Ausnahmen. Die Darstellung von Hexadezimalzahlen ist nicht Case-Sensitive. Es ist sogar eine Mischform von Klein- bzw. Grobuchstaben mglich. Folgende Codezeilen liefern das gleiche Ergebnis: 45

Das folgende Beispiel zeigt Ihnen die Berechnung des Zahlenwertes 2307 anhand der Wertigkeit der einzelnen Ziffern. Natrlich ist das Ergebnis sofort abzulesen, da wir es gewohnt sind, mit Dezimalzahlen (Zahlen zur Basis 10) zu arbeiten, ohne uns groe Gedanken darum zu machen.

Abb. 9:

Dezimalzahl

Der dezimale Wert von Hexadezimalzahlen ist dagegen nicht so ohne weiteres abzulesen. Jede Ziffer muss auf ihren Stellewert hin untersucht werden, um eine Konvertierung durchzufhren.

46

Abb. 10:

Hexadezimalzahl

Hexadezimal Dezimal

0 0

1 1

2 2

3 3

4 4

5 5

6 6

7 7

8 8

9 9

A 10

B 11

C 12

D 13

E 14

F 15

Tab. 2: Wertigkeiten

Zur Initialisierung einer Variablen in hexadezimaler Schreibweise wird das Prfix 0x vorangestellt.

In Zeile 12 wird eine Variable vom Datentyp int deklariert. Die Initialisierung in Zeile 13 findet jedoch aufgrund des Prfixes 0x mit einer Hexadezimalzahl statt. Die Ausgabe durch Zeile 14 erfolgt jedoch wie gewohnt in Form einer 47

Dezimalzahl und wir sehen, dass das o.g. Beispiel korrekt ist, denn das Programm liefert als Ergebnis 15090. Deklarierung + Initialisierung Deklarierung und Initialisierung lassen sich in eine Zeile zusammenfassen, so dass der Variablen direkt nach der Deklarierung ein initialer Startwert zugewiesen wird. Diese Vorgehensweise bietet sich natrlich an, um nicht in die o.g. Falle zu tappen und einen Fehler zu riskieren.

Zeile 13 ist die Zusammenfassung von Deklarierung und Initialisierung in einer Anweisung. Mehrere Variable gleichen Datentyps knnen in einer einzigen Anweisung deklariert werden, indem sie durch Kommata voneinander getrennt, hintereinander geschrieben werden.

Natrlich ist es auch in diesem Fall mglich, die Variablen in einer einzigen Zeile zu deklarieren und zu initialisieren.

48

In Zeile 13 werden zwei Variablen (intWert1 und intWert2) direkt mit einem Startwert versehen. IntWert3 bleibt uninitialisiert. Dotty: Was bedeuten die Schlangenlinien unterhalb einiger Variablen, die nach dem Start der Anwendung erschienen sind? Ein Blick in die Fehlerliste im Bereich Warnungen gibt Aufschluss.

Zwei Warnungen werden aufgelistet, welche zwar das Programm zur Laufzeit nicht beeintrchtigen, doch wichtige Informationen fr den Entwickler zur Verfgung stellen. Eine Variable intWert1 wurde deklariert und initialisiert, doch eine Auswertung findet in keinster Weise an irgendeiner Stelle im Code statt. Sie verbraucht Speicherressourcen, die unntigerweise bereitgestellt werden. Wir knnten die Variable gefahrlos lschen ohne den Ablauf des Programms zu beeinflussen. Die zweite Variable intWert3 wurde zwar deklariert, jedoch nie initialisiert. Sie wird, wie auch die erste Variable, nicht ausgewertet. Initialisierungszeile wieder finden Es gibt eine komfortable Mglichkeit, eine Deklarationszeile einer bestimmten Variablen zu finden und den Cursor direkt dort zu positionieren. Fhren Sie folgende Schritte aus: 49

1. Den Mauszeiger ber eine Variable positionieren 2. Mit der rechten Maustaste das Kontext-Men aufrufen 3. Den Eintrag Gehe zu Definition whlen

Der Cursor wird in die Deklarationszeile direkt vor den Variablennamen gesetzt. Aufgrund der unmittelbaren Nhe der Zeilen ist das Beispiel zwar witzlos, doch es soll Ihnen die Mglichkeit des schnellen Auffindens einer Deklaration vor Augen fhren. Mchten Sie vielleicht eine Namensnderung einer Variablen vornehmen, so geht dies am schnellsten und sichersten ber die Deklarationszeile. Die Vorgehensweise haben Sie im Kapitel der Entwicklungsumgebung unter Smarttags schon kennen gelernt.

50

Variablennamen und Prfixe


Ich hatte schon eben erwhnt, dass bei der Wahl eines geeigneten Bezeichners auf einen mglichst aussagekrftigen Namen Wert gelegt werden sollte. Warum sich das Leben unntig schwer machen und einen kryptisch anmutenden Bezeichner xf05 whlen, wenn strNachname sofort ber dessen Sinn Auskunft gibt. Es gehrt zu einem guten Programmierstil, dem Bezeichner ein so genanntes Prfix voranzustellen. In der Regel setzt es sich aus drei klein geschriebenen Buchstaben zusammen, womit sich auf den verwendeten Datentyp schlieen lsst. Natrlich handelt es sich hierbei nur um ein Vorschlag, den Sie annehmen oder ablehnen knnen. Auswirkungen auf die Stabilitt des Codes, wenn Sie sich gegen die Vorschlge entscheiden, gibt es nicht.

Prfix byt srt int lng

Datentyp byte short int long

Tab. 3: Tabelle fr Ganzzahlen

Prfix flt dbl dec

Datentyp float double decimal

Tab. 4: Tabelle fr Fliekommazahlen

51

Prfix chr str

Datentyp char string

Tab. 5: Tabelle fr zeichenbasierte Typen

Prfix bln obj

Datentyp bool object

Tab. 6: Tabelle restliche Typen

Es gibt aber noch einen anderen Trick, um sich ber den Datentyp einer Variablen zu informieren, ohne den ganzen Source-Code nach deren Initialisierung zu durchforsten. Fahren Sie mit der Maus ber einen Bezeichner und verharren dort einen Augenblick. Im folgenden Beispiel bin ich mit dem Mauszeiger ber intWert stehen geblieben.

Der Tooltip bringt es ans Licht. Es handelt sich um eine lokale Variable vom Datentyp int. Zwei Zeilen darber findet sich zwar die Antwort, doch diese unmittelbare Nhe ist in den seltensten Fllen gegeben.

52

Ausgabe eines Variableninhaltes


Ich habe bis jetzt zur Ausgabe von Text oder Variableninhalten stillschweigend eine Klasse zur Hilfe genommen. Diese Klasse mit Namen Console mchte ich an dieser Stelle ein wenig nher betrachten. Sie stellt fr uns eine Reihe von Methoden zur Verfgung, derer wir uns schon in der Vergangenheit mehrfach bedient haben: ReadLine() WriteLine() Natrlich existieren noch mehr als nur diese zwei Methoden, doch bis jetzt haben wir ihre Funktionalitt noch nicht in Anspruch genommen. C# ist, wie am Anfang schon erwhnt, eine objektorientierte Sprache. Obwohl wir nicht bewusst eine objektorientierte Programmierung an den Tag gelegt haben, verfolgt sie uns auf Schritt und Tritt. Sie fragen sich: wo? Der Punkt zwischen System und Console bzw. WriteLine() ist ein Indiz fr die OOP. Schreiben wir im Editor folgenden Code: Console. so meldet sich nach der Eingabe des Punktes ein sehr mchtiges Tool der Entwicklungsumgebung zu Wort: IntelliSense. Das sich ffnende Fenster listet automatisch alle zur Verfgung stehenden Mitglieder der Klasse Console auf.

53

Abb. 11:

Punktnotation

Ich habe absichtlich den Inhalt ganz nach unten gescrollt, um die schon bekannten Methoden ReadLine und WriteLine sichtbar zu machen. Sie knnen Sie entweder mit der Maus auswhlen oder gnzlich mit der Tastatur arbeiten, in dem Sie nur den Buchstaben r tippen. Er ist der Anfangsbuchstabe von ReadLine und wird dadurch automatisch markiert. Bereits nach der Eingabe des ersten Buchstabens werden von der Syntax her passende Wrter vorgeschlagen. Jeder neu hinzugefgte Buchstabe grenzt die Auswahl weiter ein. Jetzt nicht die Enter - Taste zur Besttigung drcken, sondern so tun, als ob ReadLine schon im Editor stehen wrde. Drcken Sie die ffnende Runde Klammer ( und ReadLine wird im Code gefolgt von der Klammer erscheinen. Durch ein wenig bung mit dem Umgang von IntelliSense, bereitet das Kodieren wirklich Spa und es erspart Ihnen eine Menge Tipparbeit. Hat sich das IntelliSense Fenster aus irgendwelchen Grnden geschlossen und im Editor steht nur noch Console gefolgt von dem Punkt, so drcken Sie die beiden Tasten (Strg + Leer) und IntelliSense meldet sich zurck. 54

Wie unschwer zu erkennen ist, besitzt jeder Listeneintrag des Auswahlfensters am linken Rand jeweils ein Icon, welches Aufschluss ber die Art des zur Klasse gehrenden Mitgliedes gibt. Sie werden sicherlich noch weitere Ihnen unbekannte Icons entdecken, die ich aber zu gegebener Zeit erlutere.

Icon

Erklrung Signalisiert, dass es sich um eine Methode handelt Signalisiert, dass es sich um eine Property (Eigenschaft) handelt

Tab. 7: Iconerklrungen

Methoden sind in der Objektorientierten Programmierung (OOP) mit Unterprogrammen in der prozeduralen Programmierung zu vergleichen. Zu Poperties (Eigenschaften) kommen wir spter, wenn wir direkt auf die OOP eingehen. Schauen Sie sich nun folgenden Code an:

Zeile Erklrung 11 Deklaration und Initialisierung der Variablen Integer-Literal 0815 12 bergabe sowohl einer Zeichenkette inklusive Formatausdruck {0}, als auch des Variablennamen an die Methode WriteLine() der Klasse Console 13 Warten auf Besttigung durch die Enter - Taste

intMeineVariable mit dem

Tab. 8: Code-Erluterungen

55

Achten Sie bitte darauf, dass die Nummerierung der Formatausdrucks innerhalb des geschweiften Klammernpaares bei 0 beginnt. Mchten Sie mehrere Werte auf der Konsole ausgeben, so schauen Sie sich folgendes Code-Beispiel an:

Die Methode WriteLine() in Zeile 12 enthlt in der Zeichenkette zwei durchnummerierte Formatausdrcke {0} und {1}. Diese werden durch die nachfolgenden Inhalte der Variablen intWert1 bzw. intWert2 in der angegebenen Reihenfolge ersetzt, so dass die Ausgabe nach dem Start des Programms Wert1= 10, Wert2= 20 lautet. Dotty: Es wurde System vor Console.WriteLine() vergessen. Ist das in Ordnung so? Gut erkannt! Der komplette Pfad ist eigentlich nicht notwendig und ich bin Ihnen bis jetzt eine Erklrung fr die ersten Zeilen der Anwendung schuldig geblieben.

Wird eine Klasse mit einer ihrer Methoden verwendet, ist die Angabe des Full Qualified Names (FQN) notwendig. bersetzt heit das: voll qualifizierter Name. Das bedeutet: Namespace.Klasse.Methode 56

So wurde es bis jetzt von mir gehalten. Da dieser Name sehr lang ist und u.U. recht unbersichtlich wird, knnen wir in C# mittels einer using-Direktive Abhilfe schaffen. Durch using System; aus Zeile 1, wird der Applikation der Namespace System bekannt gemacht und als Folge daraus, kann auf die komplette Pfadangabe (absolute Referenz) verzichtet werden. Ist Ihnen die Angabe von: Console.WriteLine() immer noch zu lang, so ist es mglich, einen Aliasnamen zu definieren.

In Zeile 3 wird fr den Klassennamen Console der Aliasname A (fr Ausgabe) festgelegt. Deshalb ist die Kurzschreibweise in Zeile 11 und 12 zulssig. Programmierern wird zwar nachgesagt, sie seien schreibfaul und dagegen ist eigentlich nichts einzuwenden. Dennoch sollte man es nicht so weit treiben, dass die Lesbarkeit darunter leidet. Entscheiden Sie selbst, ob der o.g. Code einen Vorteil bringt oder mehr Verwirrung stiftet.

57

Einlesen in eine Variable


Bis jetzt haben wir immer nur Daten an die Konsole geschickt, um diese anzuzeigen. Der umgekehrte Weg ist natrlich auch mglich. Dafr ist die Methode ReadLine() zustndig. Sie liest ein oder mehrere Zeichen aus dem Datenstrom, der ber die Tastatur an den Rechner geschickt wird.

Zeile Erklrung 11 13 Deklaration der Variablen strEingabestrom

vom Datentyp string

Der Eingabestrom wird nach dem Drcken der Enter - Taste der Variablen

strEingabestrom zugewiesen
15 16 Ausgabe des Variableninhaltes an die Konsole Warten auf Besttigung durch die Enter - Taste

Tab. 9: Code-Erluterungen

Dotty: Warum mssen wir eine Variable vom Datentyp string deklarieren? Das ist eine berechtigte Frage. Der Datentyp string besitzt die Fhigkeit den Unicode-Zeichensatz zu verarbeiten, der eine Erweiterung des ASCIIZeichensatzes darstellt. ASCII (American Standard Code for Information Interchange) besitzt eine 8-Bit (1 Byte) Kodierung und kann somit 256 verschiedene Zeichen darstellen. Diese, in Bezug auf alle mglichen Zeichen der Alphabete in der Welt, beschrnkte Kapazitt, machte eine Erweiterung notwendig. Die Folge war der Unicode-Zeichensatz mit einer 16-Bit (2 Byte) 58

Kodierung. Statt der 28 = 256 finden in ihm 216 = 65.536 unterschiedliche Zeichen Platz. Das bietet z.B. asiatischen Alphabeten mit ihren umfangreichen Zeichenstzen gengend Raum zur Speicherung. Fahren Sie doch einfach mal mit Ihrer Maus ber die Methode ReadLine in Zeile 13 und verharren dort, damit sich der Tooltip zu Wort melden kann.

In der ersten Zeile des Tooltips knnen Sie den so genannten Datentyp des Rckgabewertes der ReadLine-Methode erkennen. Er steht unmittelbar vor Console.ReadLine() und lautet string. Der Tooltip ist sogar so gesprchig und liefert eine kurze Beschreibung ber die Funktionalitt der Methode. Experimentieren Sie einfach ein wenig und Sie werden feststellen, dass die Entwicklungsumgebung an sehr vielen Stellen Tooltips zur Anzeige bringt und zustzliche Informationen zum besseren Verstndnis liefert. Dotty: Warum gibt es eigentlich unterschiedliche Datentypen? Reicht nicht ein einziger, der in der Lage ist, alles Mgliche zu speichern? Das ist natrlich machbar, doch bei einer professionellen objektorientierten Programmiersprache wie C#, wird gerade den unterschiedlichen Datentypen eine besondere Bedeutung beigemessen. Jeder Datentyp besitzt einen konkreten Wertebereich.

59

Datentypen und Wertebereiche


Wie schon anfangs erwhnt, dienen Variablen zu Speicherung von Daten. Eine Variable belegt also einen bestimmten reservierten Speicherbereich, der durch eine festgelegte Anzahl von Bytes bestimmt wird. Vergewissern Sie sich vor der Deklaration einer Variablen, welche mglichen Werte sie zur Laufzeit der Anwendung aufnehmen soll. Danach entscheiden Sie mit Hilfe der folgenden Tabelle, welcher Datentyp in Bezug auf den Wertebereich fr die Variable in Frage kommt. Mchten Sie z.B. eine Variable zur Aufnahme des aktuellen Monats (1 bis 12) deklarieren, so kann der Datentyp byte (Datenbreite: 1 Byte) vollkommen ausreichend sein, da sich sein Wertebereich von 0 bis 255 erstreckt. Whlen Sie im Extremfall long (Datenbreite: 64 Byte), so ist das etwas berdimensioniert und verschwenderisch gewhlt.
Typ .NETLaufzeittyp byte sbyte short ushort int uint long ulong float double decimal bool char Byte SByte Int16 UInt16 Int32 UInt32 Int64 UInt64 Single Double Decimal Boolean Char 0 bis 255 -128 bis 127 -32.768 bis 32.767 0 bis 65.535 -2.147.483.648 bis 2.147.483.647 0 bis 4.294.967.295 -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807 0 bis 18.446.744.073.709.551.615 1.5e-45 zu 3.4e38 (ungefhrer Bereich) 5.0e-324 zu 1.7e308 (ungefhrer Bereich) 1,0 10e28 zu 7,9 10e28 wahr oder falsch ein einzelnes Uni-Code Zeichen (0 bis 65.535) Wertebereich

60

string object

String Object

Zeichenfolge von Uni-Code Zeichen (begrenzt durch Hauptspeicher) Universeller Datentyp. Kann alle anderen Datentypen reprsentieren

Tab. 10:

Elementare Datentypen

Die in der linken Spalte aufgefhrten Datentypnamen sind Aliase, wobei die .NET Datentypen in der zweiten Spalte von links stehen. Sie sind im Namensraum System angesiedelt. Somit ist byte der Aliasname fr den .NET-Laufzeittyp System.Byte. Beide knnen gleichermaen verwendet werden und unterscheiden sich nur in der Schreibweise. Zusammenfassend erkennen wir, dass es bei den elementaren Datentypen, die die Basis bilden, acht Ganzzahltypen mit vier vorzeichenbehafteten: sbyte, short, int, long und vier vorzeichelosen: byte, ushort, uint, ulong gibt. Des Weiteren existieren zwei Gleitkommatypen float und double, sowie ein Festkommatyp decimal. Zudem steht uns ein logischer Datentyp bool, die Zeichentypen char bzw. string und letzten Endes der universale Datentyp object zur Verfgung. Dotty: Ist es denn wirklich notwendig, die gesamte Tabelle auswendig kennen? Die Tabellen auswendig zu kennen ist zwar lobenswert, jedoch nicht unbedingt notwendig. Nach dem Prinzip zu arbeiten: Ich wei ja wo es steht!, ist ganz brauchbar und praktikabel. Je lnger man programmiert, desto mehr gehen einem solche Dinge in Fleisch und Blut ber. Es gibt sogar eine Mglichkeit, die Wertebereiche der einzelnen Datentypen zur Laufzeit der Anwendung abzufragen. Schauen wir uns dazu folgenden Code an:

61

Wertebereich zur Laufzeit ermitteln

Lenken Sie Ihr Augenmerk auf die Zeile 10. Ich mchte mit der Methode WriteLine etwas an die Konsole ausgeben. In diesem Fall wird nicht ein Variablenname angegeben, sondern der Datentyp int. Der nachfolgend eingegebene Punkt ruft IntelliSense mit einer fr diesen Datentyp sinnvollen Auswahl auf den Plan. Uns interessieren zwei Eintrge: MaxValue und MinValue. Wir erkennen zudem, dass diese beiden Eintrge ein fr uns noch unbekanntes Icon besitzen.

Icon

Erklrung Signalisiert, dass es sich um eine Konstante handelt

Tab. 11:

Iconerklrung

MaxValue (Wertebereichs-Obergrenze) bzw. MinValue (WertebereichsUntergrenze) sind feste Werte, die zur Laufzeit nicht gendert werden knnen. Der Fachausdruck dafr ist Konstante. Wir kommen zu gegebener Zeit darauf zurck. In diesem Kontext liefern sie die Ober- bzw. Untergrenze des 62

Wertebereichs vom Datentyp int zurck. Setzen Sie zur bung unterschiedliche Datentypen aus der o.g. Tabelle der Elementaren Datentypen ein und ermitteln die entsprechenden Wertebereichsgrenzen. Wird einer Variablen eines bestimmten Datentyps ein Wert zugewiesen, der sich auerhalb des vorgegebenen Wertebereichs befindet, kommt es zu einem Compiler- oder Laufzeitfehler. Der folgende Code erzeugt einen Fehler, da die Variable bytWert vom Datentyp byte mit einem Wert auerhalb des zulssigen Wertebereichs von 0 bis 255 initialisiert wird.

Dotty: Warum steht bei manchen Datentypbezeichnungen ein kleines u davor? Der Wertebereich ndert sich dadurch ebenfalls. In C# gibt es sowohl vorzeichenbehaftete als auch vorzeichenlose Datentypen fr die Ganzzahl- bzw. Integer-Typen. Das kleine u ist der Anfangsbuchstabe vom englischen Wort unsigned, was bersetzt vorzeichenlos bedeutet. Der Grund, warum sich der Wertebereich im Gegensatz zum entsprechenden vorzeichenbehafteten Datentyp ndert, liegt an der Interpretation der Daten. Schauen wir uns dazu eine Variable vom Datentyp short an. Die Datenbreite betrgt 16 Bit, worauf der .NET-Laufzeittyp Int16 schon im Namen hindeutet.

63

Tab. 12:

Bitmuster 1

Vorzeichenbehaftete Integer-Datentypen, wie z.B. short, verwenden das hchstwertige Bit (MSB=Most Significant Bit) als Vorzeichen-Flag. Besitzt es den Wert 0, ist die Zahl positiv. Bei einem Wert 1, ist die Zahl negativ. Daraus folgt, dass sich der eigentliche Nutzanteil der 16 Bit auf 15 Bit reduziert. Der in der Bitmuster-Tabelle gezeigte Binrwert entspricht einem positiven Dezimalwert, da das hchstwertige Bit mit der Wertigkeit 215 den Wert 0 besitzt. Dotty: Welcher ist dann der hchstmgliche darstellbare Wert mit 15 Bit? Mit 15 Bit gibt es 215 = 32.768 unterschiedliche Bitkombinationen. Da die Zahl 0 ebenfalls eine Position belegt, ist die hchste dezimale Zahl 32.767, die mit dem Datentyp Int16 gespeichert werden kann. Kommen wir zu den negativen Zahlen, die fr das Binrsystem im so genannten Zweierkomplement abgebildet werden knnen. Bei diesem Verfahren werden alle Bits gekippt. Das bedeutet: aus 0 wird 1 und umgekehrt. Abschlieend wird binr eine 1 addiert. Wir wollen als Beispiel eine negative Binrzahl in eine Dezimalzahl umrechnen. Folgendes Bitmuster liegt vor:

64

Tab. 13:

Bitmuster 3

Gehen wir die einzelnen Schritte einmal durch: Das hchstwertige Bit ist 1. Folge: Die Zahl ist negativ. Invertieren der einzelnen Bits:

65

Addieren einer binren 1:

Ergebnis ist: 27 + 24 + 23 + 22 = -156 (dezimal) Wrden wir jetzt das gleiche Bitmuster 3 als UInt16 deklarieren, so bekmen wir das MSB, welches vorher als Vorzeichen-Flag diente, zum Nutzanteil hinzu. Jetzt stehen uns 16 statt nur 15 Bit zur Verfgung, was natrlich Auswirkungen auf den Wertebereich hat. Dieser erstreckt sich von 0 bis 65.635, was 216 mglichen Kombinationen entspricht. Stellt Ihnen jemand die Aufgabe, eine beliebige Bitkombination im Speicher zu analysieren, um den entsprechenden dezimalen Wert zu bestimmen, mssen Sie wissen, wie die Daten zu interpretieren sind. Welchem Datentyp liegt die Bitkombination zu Grunde?

Literale unter die Lupe genommen


Wie schon angedroht, werfen wir jetzt einen Blick auf die unterschiedlichen Literale, die ich im Kapitel der Variableninitialisierung vorgestellt habe. Rufen Sie sich die Bedeutung noch einmal in Ihr Gedchtnis zurck. Ein Literal ist ein einfacher Ausdruck, der sich nicht weiter zerlegen lsst. Damit sind konstante Werte gemeint, wie z.B. die Zahl 4712, die keinen Namen besitzen. Da C# eine 66

typenstrenge Sprache ist, haben natrlich auch Literale einen Typ. Doch welchen? Ganzzahl Literale Die ersten Literale, die uns ber den Weg gelaufen sind, waren die Ganzzahl Literale. Beispiele fr ganzzahlige Literale sind 0, 1024, -17, 42. Wie knnen wir jetzt feststellen, welchen Datentyp die Literale innehaben? Das folgende Programm gibt Aufschluss:

Zur Ermittlung des zugrunde liegenden Datentyps findet die Methode GetType() Verwendung. Geben Sie den o.g. Code ein und gelangen an den Punkt hinter dem Literal, sollte sich normalerweise IntelliSense melden. In diesem Fall aber nicht, da der Editor denkt, es handle sich um eine Kommazahl, die, wie wir noch sehen werden, nicht ein Komma, sondern einen Punkt enthlt. Drcken Sie einfach die beiden Tasten (Strg + Leer) und IntelliSense erscheint auf der Bildflche. Die Ausgabe besttigt, dass es sich in allen 4 Fllen um den Datentyp System.Int32 also int handelt. Hexadezimale Literale Da einem in der Programmierung nicht gerade selten hexadezimalen Zahlen ber den Weg laufen, ist es in C# ebenfalls mglich, diese zu verwenden. Ein hexadezimales Literal muss mit 0x (Null + x) beginnen. Beispiel: 0xFF Gleitkommaliterale Verwenden Sie eine Gleitkommazahl wir z.B. 34.3456, ist diese standardmig vom Datentyp double und das nachfolgende Programm besttigt diese Aussage. 67

Dotty: Es gibt aber noch andere Gleitkommatypen. Wie initialisiere ich denn eine Variable mit einem dieser Typen? Zu diesem Zweck gibt es die Typqualifizierer, die als Suffix dem Literal angehngt werden. Ein Blick in die Tabelle verschafft einen berblick ber die existierenden Suffixe fr Gleitkomma-Datentypen.
Suffix F,f D,d M,m Gleitkomma-Datentyp Float double (Default) Decimal

Tab. 14:

Typqualifizierer fr Gleitkomma-Datentypen

Mit diesem Hintergrundwissen wird Ihnen bestimmt sofort auffallen, was an folgendem Code nicht stimmt und wie Sie den Fehler beheben knnen.

Und, erkannt? Eine Dezimalzahl wird von der .NET-Laufzeitumgebung ohne Angabe eines zustzlichen Suffixes immer als zum Datentyp double zugehrig angesehen.

68

Wie sieht Ihre Fehlerbehebung aus? Wenn Sie ein F als Suffix anhngen, haben Sie gute Karten, den Fehler zu vermeiden. Zeichenliterale Der Datentyp char ist in der Lage ein einziges Unicode-Zeichen aufzunehmen. Um dies zu erreichen, wird das Zeichen von einfachen Anfhrungszeichen umschlossen. Gerade als Anfnger ist man immer geneigt, die doppelten Anfhrungszeichen, wie sie bei Zeichenketten, den so genannten Strings Verwendung finden, auszuwhlen. Das fhrt aber zu einem Fehler.

Escape-Sequenzen An dieser Stelle ist es passend, Sie auf die Escape-Sequenzen aufmerksam zu machen. Sie setzen sich aus einem Backslash \ und einem nachfolgend aufgefhrten Zeichen zusammen und werden fr die Darstellung nicht unmittelbar angebbarer Zeichen verwendet. Darunter fallen auch die Steuerzeichen wie z.B. Zeilenvorschub oder horizontaler Tabulator, um nur einige zu nennen. Die folgende Tabelle listet einige wichtige Escape-Sequenzen auf:
Escape-Sequenz \a \b \e Bedeutung Alarmsignal Backspace (Rckschritt - Taste)

Esc - Taste

69

\f \n \r \t \v \xdddd \ \ \\ \0

Seitenumbruch (Form-Feed) Neue Zeile (Zeilenvorschub, New Line) Wagenrcklauf (Carriage Return) horizontaler Tabulator vertikaler Tabulator Zeichen (dddd = Hex-Code des Zeichens) Einfaches Anfhrungszeichen innerhalb einer Zeichenkette Doppeltes Anfhrungszeichen innerhalb einer Zeichenkette Backslash Null-Zeichen

Tab. 15:

Escape-Sequenzen

Der nun folgende Code schaut vielleicht etwas merkwrdig aus, doch er zaubert einen durchaus lesbaren Text auf die Konsole.

Zeile Erklrung 12 13 Schreibt die Zeichenkette Hallo inklusive Zeilevorschub auf die Konsole Die Escape-Sequenz \t bewirkt einen horizontalen Tabulator bevor die Zeichenkette Programmierer! inklusive Zeilenvorschub an die Konsole geschickt wird 14 Zeichenketten werden in doppelte Anfhrungszeichen gesetzt. Mchten Sie jedoch

70

innerhalb der Zeichenkette ein doppeltes Anfhrungszeichen setzten, so ist das nicht so ohne weiteres mglich, da es das Ende der Zeichenkette kennzeichnen wrde. Zur Maskierung, verwendet man den davor gesetzten Backslash \. Auf diese Weise erreichen wir die Anfhrungszeichen vor und hinter C# 15 Diese Zeile sieht auf Anhieb etwas kryptisch anmutend aus. Doch ein Blick in die Tabelle zeigt uns, dass sich hinter der Escape-Sequenz \x004E ein Hex-Code Zeichen verbirgt. Der ASCII-Code 4E bringt das Zeichen N zum Vorschein. Abschlieend erzeugt das Zeichen \a ein akustisches Signal, welches ber den PC-Lautsprecher ausgegeben wird.

Tab. 16:

Code-Erluterungen

Stringliterale Unter einem String oder Zeichenkette versteht man eine Abfolge von Zeichen, die von doppelten Anfhrungszeichen eingeschlossen sind. In vorangegangenen Beispielen sind uns Strings mit der Methode WriteLine() des fteren begegnet. Natrlich darf sich ein String, wie schon im letzten Beispiel gezeigt, auch aus mehreren Escape-Sequenzen zusammensetzen. "Ich bin ein Sequenzen." Boolesche Literale Den Datentyp Boolean oder kurz bool haben wir bis jetzt noch nicht nher betrachtet. Existiert eine Variable dieses Typs, so kann sie nur zwei Zustnde true oder false einnehmen. Das heit bersetzt wahr oder falsch. Diese Zustnde spielen bei so genannten Kontrollstrukturen, wenn es also um die Bewertung einer Bedingung (z.B. ist a > b?) geht, eine groe Rolle. \nString mit \teinigen Escape-

71

Dotty: Woher kommt eigentlich der Name Boolesche? Nun, es gab da einen englischen Mathematiker mit dem Namen George Boole (1815 - 1864). Er war der Begrnder der mathematischen Logik und er entwickelte die nach ihm benannte Boolesche Algebra, mit deren Hilfe sich Digitalschaltungen berechnen und vereinfachen lassen. Und da Digitalschaltungen nur zwei Zustnde (0 und 1) kennen, hat man sich in der Programmierung dazu entschieden, einen speziellen Datentyp mit Namen Boolean zu kreieren, der ebenfalls nur zwei Zustnde true oder false kennt.

Was fr ein Typ bist Du?


In der Programmiersprache C# spalten sich die Datentypen in zwei unterschiedliche Lager auf. Der Unterschied zwischen beiden ist in der Art der Speicherung zu finden, die der Stack bzw. Heap den Daten bietet. Wertetyp Da ist zum einen der Wertetyp. In diese Kategorie fallen alle elementaren Datentypen aus der schon gezeigten Tabelle, bis auf die Datentypen string und object. Wertetypen werden in einem Speicherbereich abgelegt, der sich Stack nennt und im Hauptspeicher seinen Platz findet. Stack bedeutet bersetzt Stapel. Stellen wir uns z.B. einen Stapel der aus einigen Kartons besteht vor.

72

Wird etwas darauf abgelegt, so erfolgt das in der Regel On Top, also oben drauf. Umgekehrt bedeutet das Wegnehmen vom Stapel ein Abholen der obersten Kiste. Dieses Verfahren wird LIFO (Last-In-First-Out) genannt. Was zuletzt oben drauf gepackt wurde, wird beim Abholen zuerst wieder weggenommen. Zustndig fr das Handling des Ablegens bzw. Abholens ist der so genannte Stackpointer, der durch den Prozessor gesteuert wird. Er reserviert neuen Speicher oder gibt ihn ggf. wieder frei. Die Funktionsweise des Stacks erlaubt keinen wahlfreien Zugriff auf die Daten, so dass sie nicht aus der Mitte des Stapels entnommen werden knnen. Folgende Codezeile legt einen Integerwert mit einer Datenbreite von 32-Bit auf dem Stack ab. int intWert = 42; Verweis- bzw. Referenztyp Zum anderen gibt es den Verweis- bzw. Referenztyp. Die Datentypen string oder object fallen in diese Kategorie. Verbirgt sich hinter einer Wertetyp-Variablen der eigentliche Wert, der ihr bei der Initialisierung zugewiesen wurde, beinhaltet eine Verweistyp-Variable lediglich einen Zeiger auf einen bestimmten Speicherbereich. Dieser Bereich wird Heap genannt und ist ebenso wie der Stack im Hauptspeicher angesiedelt. Es handelt sich dabei um einen dynamischen Speicher, der vom System verwaltet wird und nur so weit wachsen kann, wie es der maximale Arbeitsspeicher zulsst. Der Zugriff ist im Gegensatz zum Stack wahlfrei und ermglicht es, Daten auch aus der Mitte des Heaps zu erreichen. Dotty: So wie ich das verstehe, teilt sich bei Verweistypen die Speicherung der Daten in zwei Teile auf. Es gibt einen Zeiger und einen Speicherbereich, auf den dieser Zeiger verweist. Wo liegen jetzt diese beiden Elemente? Gut aufgepasst! Schauen wir uns das folgende Diagramm genauer an:

73

Abb. 12:

Speicherung von Verweistypen

Wie wir sehen, existiert eine Wertetyp-Variable intWert auf dem Stack. Sie beinhaltet den Zahlenwert 23. Es gibt eine weitere Wertetyp-Variable chrWert mit dem Zeichen a. Doch jetzt kommen wir zu einem Verweis, der sich auf dem Stack befindet, jedoch mit seiner Adresse auf einen Speicherbereich des angesprochenen Heaps zeigt. Dort befindet sich der eigentliche Inhalt der Verweis-Variablen strW.

Typumwandlungen
Sie haben bei der Initialisierung von Variablen gesehen, dass eine Zuweisung z.B. durch ein Literal mglich ist:

74

Mit der Anweisung in Zeile 12 wird einer Integervariablen der Wert 42 zugewiesen. Natrlich ist es auch mglich den Inhalt einer Variablen einer anderen zuzuweisen, wie das folgende Beispiel zeigt:

Wie in vorherigen Beispiel wird eine Variable in Zeile 12 durch ein Literal initialisiert. Diese wird aber jetzt dazu genutzt, um eine weitere Variable gleichen Datentyps int mit einem Wert zu versehen. Da beide Variablen dem gleichen Datentyp int angehren, welcher eine Datenbreite von 32 Bit vorweist, gibt es beim Kopieren keine Probleme. Die treten mglicherweise erst dann auf, wenn versucht wird, Variablen unterschiedlicher Datentypen einander zuzuweisen. Die beiden folgenden Grafiken werden den Sachverhalt hoffentlich ein wenig klarer erscheinen lassen.

75

Abb. 13:

Konvertierung bei unterschiedlichen Datentypen

Nehmen wir an, es existieren zwei Variablen unterschiedlichen Datentyps. Die eine ist vom Typ int, whrend die andere vom Typ long ist. Beide besitzen unterschiedliche Datenbreiten.
Datentyp int long Datenbreite 32 Bit 64 Bit

Tab. 17:

Datentyp und Datenbreite

Das Fassungsvermgen einer Variablen vom Datentyp long ist erwartungsgem hher, als das der Variablen vom Typ int. Die hher dimensionierte Variable htte demnach also keine Probleme bei der Aufnahme der Daten der niedriger dimensionierten. Implizite Konvertierung

76

Zeile Erklrung 12 Deklaration und Initialisierung der Variablen dem Integer-Literal 32589 13

intVariable1 vom Datentyp int mit

lngVariable2 vom Datentyp long und einer Initialisierung durch die Variable intVariable1 aus Zeile 12. Es findet eine
implizite Konvertierung statt, die der Compiler in Eigenregie durchfhrt, da kein Datenverlust zu befrchten ist.

Deklaration einer Variablen

Tab. 18:

Code-Erluterungen

Zuweisungen von Variablen unterschiedlicher Datentypen bedeutet immer eine Konvertierung des auf der rechten Seite des Zuweisungsoperators stehenden Datentyps in den auf der linken Seite befindlichen. Die Datentyp-Pyramide zeigt das implizite Konvertieren eines GanzahlDatentyps in Pfeilrichtung in den darunter liegenden. Wir erkennen auf den ersten Blick, dass der jeweils unterhalb angeordnete Typ das Fassungsvermgen besitzt, den oder die darber liegende(n) aufzunehmen. Aus diesem Grund schlgt eine implizite Konvertierung nie fehl.

Abb. 14:

Datentyp-Pyramide

77

Die folgende Tabelle zeigt die mglichen impliziten Konvertierungen elementarer Datentypen. Die in runden Klammerpaaren enthaltene Zahl, gibt die Datenbreite der Datentypen in Bits an:
Von byte (8) sbyte (8) short (16) ushort (16) int (32) uint (32) long (64) ulong (64) float (32) double (64) decimal (128) bool (8) char (16) string object Nach short, ushort, int, uint, long, ulong, float, double oder decimal short, int, long, float, double oder decimal int, long, float, double oder decimal int, uint, long, ulong, float, double oder decimal long, float, double oder decimal long, ulong, float, double oder decimal float, double oder decimal float, double oder decimal double ushort, int, uint, long, ulong, float, double oder decimal -

Tab. 19:

Implizite Konvertierungsmglichkeiten

Hier nun einige Beispiele fr implizite zulssige Konvertierungen:

78

Und hier einige, die einen Fehler erzeugen:

Der umgekehrte Fall ist natrlich auch mglich. Wird versucht, ein Variableninhalt einer Variablen mit grerer Datenbreite in eine Variable mit geringerer Datenbreite zu kopieren, gibts Probleme. Das Fassungsvermgen einer Integervariablen reicht eben nicht aus, um eine Longvariable aufzunehmen.

79

Abb. 15:

Konvertierung bei unterschiedlichen Datentypen

Das folgende Codebeispiel zeigt uns, dass eine Zuweisung auf die gezeigte Weise nicht funktioniert.

Unser inzwischen schon geschultes Auge erkennt sofort die Schlangenlinien unterhalb der Variablen lngVariable1 und ein weiterer Blick in die Fehlerliste lsst nichts Gutes ahnen.

80

Abb. 16:

Fehlerlisteninhalt fr Konvertierungsprobleme

Zwar ist die Zahl 32589 nicht so gro, dass sie nicht in eine Variable vom Typ int passen wrde. Wir erinnern uns an den Wertebereich des Datentyps int. Er erstreckt sich von -2.147.483.648 bis 2.147.483.647. Sollte also locker unter zu bringen sein. Dennoch bekommen wir eine Fehlermeldung vor den Latz geknallt, da eine implizite Konvertierung lt. Datentyp-Pyramide nur in Pfeilrichtung mglich ist. Da die angesprochene Konvertierungsform ausdrcklich implizit genannt wurde, liegt der Verdacht nahe, dass es noch mindestens eine weitere gibt. Und so ist es auch. Es gibt noch eine. Explizite Konvertierung (Casting) Die Explizite Konvertierung, auch Casting genannt, ist ein ausdrcklicher Befehl an den Compiler, einen Datentyp in einen anderen zu konvertieren. Vielleicht haben einige von Ihnen schon mit der Programmiersprache C oder C++ gearbeitet. Bei diesen Programmiersprachen existiert ein so genannter Cast-Operator, der die Umwandlung eines Datentyps erzwingt. Die Syntax schreibt vor, den Ziel-Datentyp, in den die Konvertierung stattfinden soll, dem ursprnglichen Datentyp in runde Klammern unmittelbar davor zu setzen. (Zieldatentyp)Variable Eine explizite Konvertierung ist immer dann von Nten, wenn ein Wert in eine Variable kopiert werden soll, die eine geringere Datenbreite als die Ursprngliche aufweist. Also z.B. der Datentyp long mit einer Datenbreite von 64 Bit in den Datentyp int mit einer Datenbreite von 32 Bit.

81

Ohne das Casting in Zeile 13 wrden wir einen Fehler bekommen, da eine implizite Konvertierung nicht mglich ist. Dotty: Passt der zu konvertierende Wert in den Zieldatentyp, ist alles in Butter. Doch was passiert, wenn dem nicht so ist und aufgrund der geringeren Datenbreite der Wert nicht komplett aufgenommen werden kann? Fehler beim Casting Das Verhalten knnen wir uns am besten an einem Beispiel ansehen,

indem wir den Code analysieren und uns die Speicherung der Daten auf unterster - also auf Bitebene - anschauen.
Zeile Erklrung 12 Deklaration und Initialisierung der Variablen mit dem Literal 30365 13 Deklaration einer Variablen

srtVariable1 vom Datentyp short

Initialisierung durch die Variable

bytVariable2 vom Datentyp byte und einer srtVariable1 aus Zeile 12. Es wird durch das

82

Casting eine explizite Konvertierung erzwungen, da der Ziel-Datentyp eine geringere Datenbreite aufweist als der Quelltyp. 14 Es wird der Wert der Variablen bytVariable2 ausgegeben

Tab. 20:

Code-Erluterungen

Fhren Sie den Code aus, erhalten Sie die folgende Ausgabe auf Ihrer Konsole: 157 Donnerwetter!, sagen Sie sich bestimmt. Wie kommt denn dieser Wert zustande? Ist die Zahl 157 in irgendeiner Weise in der Zahl 30365 enthalten? Auf den ersten Blick ist das zweifellos nicht zu erkennen. Wir kommen nicht umhin, uns die Datenspeicherung auf Bitebene anzuschauen.

Abb. 17:

Konvertierung von Datentyp short in byte

Die oberste Bitkombination entspricht dem Speicherabbild der Variablen srtVariable1 mit dem Datentyp short und einer Datenbreite von 16 Bit. Es handelt sich um einen vorzeichenbehafteten Datentyp mit dem Wertebereich von -32.768 bis +32.767. Da das MSB (Most Significant Bit) den Wert 0 innehat, haben wir es mit einen positiven Zahl zu tun, was dem dezimalen Wert von 30.365 entspricht. Durch die explizite Konvertierung muss die vorliegende Bitkombination in eine Variable vom Datentyp byte mit der Datenbreite 8 Bit konvertiert werden. Das gelingt natrlich nicht, was einen berlauf zur Folge 83

hat. In einen Bus mit 8 Sitzpltzen finden keine 16 Personen Platz und gestanden wird nicht. Die berzhligen 8 bleiben auen vor. Genau in dieser Weise wird verfahren. Angefangen vom niederwertigsten Bit mit der Wertigkeit 20, also das auf der rechten Seite, wird Bit fr Bit in die Zielvariable bytVariable2 kopiert. Nach dem 8. Bit ist Schluss, da eben nicht mehr Platz zur Verfgung steht. Die bertragene Bitkombination entspricht dem dezimalen Wert 157. Ist doch gar nicht so schwer, oder? Fehler bei Expliziter Konvertierung erkennen Dotty: Ist zwar alles schn und gut, doch es kann nicht im Sinne des Erfinders sein, wenn ich als Programmierer von einem etwaigen berlauf nichts mitbekomme und denke, dass alles wunderbar funktioniert, dem aber nicht so ist! Vllig korrekt. Die Hilfe naht! Es gibt nmlich eine Mglichkeit, auftretende Konvertierungsfehler zu erkennen und ggf. darauf zu reagieren. Dazu wird die zu berwachende Anweisung in einen checked-Block gesetzt, der die Aufgabe der berlaufsberwachung bernimmt.

Da wir nur eine einzige Konvertierung zu berwachen haben, wird eigentlich kein checked-Block bentigt und die berprfung reduziert sich auf eine einzige Zeile: byte bytVariable2 = checked((byte)srtVariable1); 84

Fhren wir unter diesen Bedingungen das modifizierte Programm aus, dauert es einen kurzen Moment und das Ergebnis ist ein Laufzeitfehler, der aufgrund einer Exception (Ausnahme) zustande gekommen ist.

Abb. 18:

berlauf-Fehlermeldung

Eine Anwendung auf diese Weise unkontrolliert zu beenden ist unschn und unprofessionell. Doch leider reicht unser derzeitiger Kenntnistand (noch) nicht aus, entsprechend darauf zu reagieren, d.h. eine Ausnahmebehandlung auf die Exception OverflowException zu programmieren. Ein entsprechendes Kapitel wird sich mit dieser Thematik annehmen.

85

Konvertierungsmethoden

Die explizite Konvertierung mittels Casting ist nicht die einzige Mglichkeit einen Datentyp in einen anderen zu berfhren. Die .NET-Klassenbibliothek stellt diverse Konvertierungsmethoden zur Verfgung, die im Namespace System beheimatet sind.
Methode ToByte() ToSByte() ToInt16() ToUInt16() ToInt32() ToUInt32() ToInt64() ToUInt64() ToSingle() ToDouble() ToDecimal() ToBoolean() Alias Ziel-Typ byte sbyte short ushort int uint long ulong float double decimal bool

86

Tab. 21:

Konvertierungsmethoden

Das folgende Programm zeigt die beiden gleichwertigen Konvertierungsanweisungen mittels Casting bzw. Convert-Methode.

Dotty: Wenn beide gleichwertig sind, warum gibt es dann zwei Varianten? Natrlich gibt es einen gravierenden Unterschied. Wenn wir den Initialisierungswert fr die Variable srtVariable1 in Zeile 11 von 128 auf 30365 setzten und damit einen berlauf provozieren, erhalten wir mit der Casting-Anweisung aus Zeile 13 keinen Fehler. Die Konsolenausgabe erfolgt, wie schon vorher gesehen, mit einem Wert von 157, da kein checked-Block die Konvertierung berwacht. Jedoch sind alle Konvertierungsmethoden in der Lage, einen berlauf festzustellen und als Resultat davon eine Exception auszulsen. Nach dem Programmstart erscheint also der Zahlenwert 157 im Konsolenfenster und im Anschluss erfolgt eine Unterbrechung aufgrund der Exception aus Zeile 16 (Convert.ToByte(strVariable1)). Da Sie jetzt die Konvertierungsmethoden kennen gelernt haben, kommen wir zu einem schon erwhnten Punkt zurck. Die Methode ReadLine() liest die nchste Zeile von Zeichen aus dem Standardeingabestrom, wobei der Rckgabewert vom Datentyp string ist. Mchten Sie ein Programm schreiben, das einen numerischen Wert vom Benutzer erwartet, bieten sich die Konvertierungsmethoden an.

87

Zeile Erklrung 11 12 13 Deklaration der Variablen intEingabe vom Datentyp int Ausgabe einer Zeichenkette ohne Zeilenvorschub Existieren wie in diesem Fall verschachtelte Methoden, so gehen Sie zum besseren Verstndnis von Innen nach Auen vor.

Console.ReadLine() ist dabei die innere und Convert.ToInt32() die uere Methode. Console.ReadLine() liefert als Rckgabe einen Wert vom Datentyp string,
wiederum als bergabewert bzw. Argument der Methode

welcher

Convert.ToInt32() fungiert. Das Ergebnis der Konvertierung, ein Wert des Datentyps int, wird durch den Zuweisungsoperator = der Variablen intEingabe auf
der linken Seite zugewiesen. 14 Ausgabe der in Zeile 13 initialisierten Variablen auf die Konsole

Tab. 22:

Code-Erluterungen

Doch Vorsicht! Verwenden Sie bei der Eingabe nicht ausschlielich die Ziffern 0 bis 9, wird eine FormatException ausgelst und das Programm verabschiedet sich sang- und klanglos.

88

Arrays
Ich mchte Ihnen jetzt eine Sonderform der Variablen vorstellen. Die Variablen, die wir bis jetzt kennen gelernt haben, waren in der Lage, nur einen einzigen Wert eines bestimmten Datentyps zu speichern. Arrays oder auch Datenfelder genannt, knnen mehrere Werte des gleichen Datentyps verwalten. Es handelt sich bei dieser Speichervariante um eine Sammlung von Variablen, die ber einen einzigen Bezeichner angesprochen werden. Dotty: Wie sollen wir dann aber auf die einzelnen Werte der Variablen zugreifen, wenn nur ein einziger Variablenname existiert? Ich war mit einer Erklrung noch nicht fertig, denn es fehlt im Gegensatz zu normalen Variablen aus dem vorangegangenen Kapitel ein entscheidender Zusatz. Jeder einzelne Wert dieser Ansammlung von Variablen wird im Zusammenhang mit Arrays Element genannt. Damit der eindeutige Zugriff auf ein bestimmtes Element gelingt, wird zum eigentlichen Wert ein Index angelegt, ber den der Wert zu erreichen ist.

Dieser Broschrank besitzt 6 Schubladen, die durch einen Index in Form von kleinen Schildchen von 1 bis 6 organisiert sind. Mit der Angabe des Indexes ist 89

der Zugriff auf eine Schublade mit deren Inhalt eindeutig. Der Vorteil gegenber Variablen ist die einfache Speicherung fast beliebig groer Datenmengen bzw. der schnelle Zugriff auf einzelne Elemente. Wird fr die Adressierung der Elemente ber den Index eine Variable eingesetzt und dieser Wert kontinuierlich erhht oder vermindert, ergibt sich darauf eine komfortable und leichte Handhabung. Es sei an dieser Stelle genannt, dass ein Array ein Objekt darstellt und demnach ein Verweis- bzw. Referenztyp vorliegt.

Eindimensionale Arrays
Wie sieht also die Syntax zur Deklaration eines Arrays aus? Es wird hinter der Angabe des Datentyps ein eckiges Klammernpaar angehngt. Das folgende Beispiel deklariert ein Array vom Datentyp int. Deklaration int[] meinArray;

Ihnen ist sicherlich sofort aufgefallen, dass bei dieser Deklaration noch keine Angabe zur Gre des Arrays gemacht wurde. An dieser Stelle muss ich eine Anleihe aus der noch zu besprechenden objektorientierten Programmierung machen. Weil wir es hier nicht mit einfachen bzw. elementaren Datentypen zu tun haben, gestaltet sich die Initialisierung ein wenig anders. Durch eine Deklaration wird noch kein Objekt erzeugt. Initialisierung meinArray = new int[5];

Diese Anweisung initialisiert das Array, welches 5 Elemente besitzt. Das Schlsselwort new wird dazu verwendet ein Objekt auf dem Heap zu erzeugen. 90

Erst danach steht der Speicherbereich fr das Array zur Verfgung. Demzufolge ist die numerische Angabe innerhalb der eckigen Klammern eine Grenfestlegung des Arrays. Jedes einzelne Element ist mit dem numerischen Wert 0 initialisiert worden. Ab diesem Zeitpunkt knnen wir damit beginnen, das Array mit Daten zu versorgen. Schauen wir uns dazu das folgende Programm an:

Zeile 12 14

Erklrung Deklaration des Arrays meinArray vom Datentyp int Initialisierung des Arrays mit 5 Elementen (Alle Elemente erhalten eine Vorinitialisierung mit dem numerischen Wert 0.

16 bis 20

Das Array wird mit verschiedenen Werten beschickt

Tab. 23:

Code-Erluterungen

Dotty: Etwas verstehe ich noch nicht so ganz. Ich htte erwartet, dass der hchste Indexwert 5 betrgt. Dem ist aber nicht so. 91

Mit der Initialisierung wird die Anzahl der Elemente eines Arrays festgelegt, nicht der oberste Indexwert! Die Differenz resultiert aus der Tatsache, dass der Startindex mit dem Wert 0 beginnt.

Der kleinste Arrayindex ist immer 0 und der maximale immer um 1 kleiner als die festgelegte Arraygre. Versuchen Sie auf ein Element eines Arrays mit einem auerhalb des zulssigen Wertebereichs liegenden Indexwertes zuzugreifen, ist die Folge ein Laufzeitfehler vom Typ IndexOutOfRangeException. Deklaration + Initialisierung Deklaration (Zeile 12) und Initialisierung (Zeile 14) knnen auch hier zu einer einzigen Anweisung zusammengefasst werden: int[] meinArray = new int[5]; Im letzten Code-Beispiel wurde das Array mit einzelnen Werten (Zeile 16 bis 20) initialisiert. Falls jedoch zum Zeitpunkt der Deklaration die Werte bekannt sein sollten, knnen Sie der Deklarationszeile die Daten anfgen. Der nun folgende Source-Code erfllt die gleiche Aufgabe, wie der zuvor gezeigte:

Sie werden bemerken, dass keine Angabe ber die Gre des Arrays gemacht wurde und dennoch lsst sich das Beispiel problemlos kompilieren. Der Compiler berechnet das Array anhand der Anzahl der angefgten Elemente. 92

Zeile 11 knnten wir auch mit expliziter Angabe der Arraygre folgendermaen kodieren:

Arraygrenzen zur Laufzeit bestimmen


Ist ein Array von relativ geringer Gre und Sie mchten die einzelnen Elemente hintereinander ausgeben, spricht sicher nichts dagegen, das Zeile fr Zeile wie im folgenden Beispiel zu kodieren:

Bei einem Array von einer Gre von ber 100 Elementen knnte sich die Sache als etwas mhsam herausstellen. An dieser Stelle muss ich erneut eine Anleihe aus einem noch kommenden Kapitel machen. Diesmal beim Programmschleifenkapitel. Schleifen sind ein Konstrukt um Anweisungen wiederholt auszufhren. Genau dieser Funktionalitt bedienen wir uns hier. Schauen Sie sich dazu den letzten Source-Code genauer an. Was fllt Ihnen in Hinblick auf mgliche nderungen auf, die sich von Zeile zu Zeile ergeben? Welche Gemeinsamkeiten sind vorhanden? Das ist der Ansatz, den wir zur Realisierung der bentigten Schleife brauchen. Die 5 Zeilen, in denen durch die Methode WriteLine() die einzelnen Array-Elemente ausgegeben werden, unterscheiden sich nur an der Stelle, an der der Index jeweils um den Wert 1 erhht wird. Ein besseres Einsatzgebiet fr Schleifen kann es nicht geben. Doch jetzt erst einmal zurck zur Bestimmung der Arraygrenzen, die wir fr den Schleifendurchlauf unbedingt bentigen. Da sich die Untergrenze immer bei Indexwert 0 befindet, konzentrieren wir uns auf die Obergrenze. Schauen Sie sich folgenden Code an: 93

GetUpperBound()

Nach der Eingabe des Arraynamen gefolgt von dem Punkt, hat sich IntelliSense gemeldet und bietet einige Methoden und Eigenschaften zur Auswahl an, da alle Arrays von der Klasse Array abgeleitet sind. Mit ihrer Hilfe knnen wir u.a. einige Statusinformationen abrufen. Und siehe da, es existiert eine Methode mit Namen GetUpperBound. Sie ermittelt den maximalen Indexwert, der fr das angegebene Array zulssig ist. Es wird nicht die Anzahl der Elemente zurckgegeben.

94

Was denken Sie, liefert die Methode zurck? Den Wert 4 oder 5? Die Methode GetUpperBound() bentigt zur Berechnung einen Wert, der angibt, fr welche Dimension der maximale Indexwert ermittelt werden soll. Fr eindimensionale Arrays betrgt dieser Wert 0.

Da die Zhlweise, wie schon erwhnt, bei 0 beginnt, liegt die Array-Obergrenze bei 4. Mit diesem Wert knnen wir arbeiten, um alle Elemente mittels einer Programmschleife zur Anzeige zu bringen. Der nun folgende Code soll Sie aufgrund der neunen Komplexitt nicht erschrecken. Ich gehe jede wichtige Zeile mit Ihnen durch, so dass keine Unklarheiten brig bleiben sollten.

Zeile 11

Erklrung Deklaration und Initialisierung des Arrays nachfolgend aufgefhrten Werten.

meinArray vom Datentyp int mit

12

Deklarierung einer Variablen nachfolgender Initialisierung

intMaxIndex vom Datentyp int und


durch den Rckgabewert der Methode

GetUpperBound. Sie ermittelt den maximalen Indexwert, der fr das Array zulssig ist. Die Dimensionsangabe durch den Wert 0 besagt, dass es sich um ein eindimensionales Array handelt. 13 Deklarierung einer so genannten Laufvariablen, die in der nachfolgenden

95

Programmschleife genutzt wird, um die einzelnen Array-Elemente zu adressieren. Ihr Wert wird bei jedem Schleifendurchlauf um den Wert 1 erhht. 14 Anwendung einer for-Schleife. Die Anzahl der Schleifendurchlufe werden durch die Laufvariable i bestimmt. Syntax der for-Schleife: for ( Initialisierung; Bedingung, Laufvariablennderung). Die Initialisierung der Variablen i erfolgt mit dem Wert 0. Das ist der Start-Indexwert. Bei jedem Schleifendurchlauf wird die folgende Bedingung i <= intMaxIndex bewertet, die sich in sprachlicher Form ausgedrckt immer wieder aufs Neue fragt: Liegen wir noch unterhalb der Array-Obergrenze?. Solange das Ergebnis als wahr erkannt wird, erfolgt der nchste Schleifendurchlauf. Das ist der Fall, wenn i kleiner oder gleich dem maximal ermittelten Indexwert ist. Abschlieend wird die Laufvariable noch um den Wert 1 erhht, was mittels der Anweisung i++ erfolgt. Die for-Schleife fhrt die ihr unmittelbar in der nchsten Zeile folgenden Anweisung aus. Dann geht es in die nchste Schleifen-Runde. Wird die Bedingung i <= intMaxIndex als falsch bewertet, bricht die Schleife ihre Durchlufe ab und der Ablauf des Programms wird bei Zeile 16 fortgefhrt. 15 Ausgabe des Array-Element Namen und dessen Inhalt.

Tab. 24:

Code-Erklrungen

Mehrdimensionale Arrays
Bis jetzt haben wir das Array nur in der ersten Dimension kennen gelernt. Eine einzige Dimension stellt im geometrischen Sinn eine Gerade dar. Alle Daten eines eindimensionalen Arrays sind wie auf einer Perlenkette aufgereihte Werte. Damit wir uns entlang der Geraden bewegen knnen, ist ein einziger Indexwert notwendig. Er legt die Position der Daten in der einen existierenden Dimension fest.

96

Jetzt stellen Sie sich ein schachbrettartiges Raster vor. Es besteht aus einer Anordnung von 8 mal 8 einzelnen Feldern, ber die ein Koordinatensystem gelegt wurde. Jedes einzelne Feld ist ber eine Zeilen/Spalte-Koordinate zu adressieren bzw. zu erreichen.

Abb. 19:

Raster mit Koordinaten

Zur eindeutigen Adressierung eines Feldes brauchen wir mehr als nur einen Indexwert. Es wird sowohl eine Wert fr die y-Richtung (vertikale), als auch ein 97

zweiter fr die x-Richtung (horizontale) bentigt. In der Abbildung des Rasters habe ich ein Feld mit einem x markiert. Die Koordinaten dieses Feldes lauten (2,3). Obwohl die Festlegung vertauschbar ist, habe ich den Zeilen-Wert 2 an erster Stelle genannt. Er gibt die horizontale Zeile an, wogegen der SpaltenWert 3 die vertikale Spalte festlegt. Der Schnittpunkt beider befindet an der mit x markierten Stelle.

Der Source-Code fr die Deklarierung eines zweidimensionalen Arrays in Form eines Schachbrettes mit 8 Spalten und 8 Zeilen lautet folgendermaen: int meinArray[,] = new int[8,8]; Wenn wir das letzte Koordinatenbeispiel noch mal aufgreifen und die Koordinate (2,3) mit dem Wert 1 initialisieren, dann schreiben Sie: meinArray[2,3] = 1; Die Initialisierung mehrdimensionaler Arrays kann natrlich auch in der Deklarationszeile erfolgen. Der Einfachheit halber mchte ich folgende 3 x 3 Matrix in ein Array berfhren:

Abb. 20:

Matrix

98

An dieser Stelle mchte ich eine erneute Anleihe machen. Diesmal aus dem Kapitel ber das Debugging. Beim Debugging geht es darum, einen Fehler in einem Programm ausfindig zu machen. Fehler lassen sich grob in syntaktische und logische Fehler unterteilen. Syntaktische Fehler sind leichter zu lokalisieren, da der Compiler uns direkt darauf hinweist, dass wir z.B. eine zuvor deklarierte Variable falsch geschrieben haben und er Sie aufgrund der fehlerhaften Schreibweise nicht (er)kennt oder ein Semikolon vergessen wurde. Schwerwiegender sind die logischen Fehler. Die Aufsprung gestaltet sich erfahrungsgem als nicht so einfach, da sie sich nur derart zu erkennen geben, dass ein Programm nicht genau das macht, was von ihm erwartet wird. Doch jetzt zum versprochenen Debugging. Damit ich mir z.B. den Inhalt einer Variablen genauer anschauen kann, muss ich meine Anwendung dazu bewegen, an einer bestimmten Stelle die Ausfhrung zu unterbrechen. Damit das passiert, kann der Entwickler, also Sie, so genannte Haltepunkte einfgen. Da die letzte Anweisung im letzten Programm in Zeile 15 steht, setzten wir an diese Stelle den Haltepunkt. Dazu klicken Sie mit der linken Maustaste im Editor in den grauen Bereich links neben den Zeilennummern der entsprechenden Zeile, in der der Halt erfolgen soll.

99

Es erscheint ein roter Kreis. Der in dieser Zeile befindliche Code wird zudem rot hinterlegt, falls die Standardeinstellungen des Editors nicht abgendert wurden. Der Code in der Haltepunktzeile wird beim Erreichen noch nicht zur Ausfhrung gebracht. Starten Sie jetzt Ihre Anwendung, werden alle Zeilen bis auf Zeile 15 ausgefhrt. Dort wird die Ausfhrung unterbrochen, was nicht mit einer Programmbeendigung gleichzusetzen ist. Jetzt ist es uns mglich, z.B. den Inhalt von Variablen zu untersuchen. Alles verhlt sich so, als wenn die Zeit stehen geblieben und der momentane Status eingefroren wre. Schauen Sie sich den Fensterbereich genauer an, an dem sich normalerweise die Fehlerliste befindet. Dort ist ein Reiter mit der Bezeichnung Lokal aufgetaucht.

Abb. 21:

Variableninhalte erforschen

100

Dort sehen Sie u.a. die Variable meinArray. Wenn Sie auf das kleine Pluszeichen vor dem Namen klicken, erweitert sich die Anzeige und die Elemente mit ihren Werten werden sichtbar. Vergleichen Sie jetzt einmal die dort befindlichen Werte mit denen aus dem Source-Code. Es sind die gleichen. Mchten Sie den Haltepunkt wieder entfernen, klicken Sie einfach auf ihn und er verschwindet, so dass das Programm beim nchsten Start ohne Unterbrechung laufen sollte. Alles weitere zum Debugging im entsprechenden Kapitel. Natrlich sind auch Arrays mit noch mehr Dimensionen realisierbar. Fgen wir zu den bisherigen zwei Dimensionen eine weitere hinzu, ist das Ergebnis bei gleichen Dimensionsparametern ein Wrfel. int meinArray[,,] = int[3,3,3];

Ungleichfrmige Arrays
Enthlt jede Dimension eines Arrays die gleiche Anzahl von Elementen, wird das Array gleichfrmig genannt: int meinArray[,] = int[7,7]; Diese Arrays haben eine rechteckige bzw. quadratische Form.

Jetzt besitzt C# aber die Mglichkeit, Arrays zu deklarieren, die eine unterschiedliche Anzahl von Elementen pro Dimension besitzen. Diese Arrays werden Ungleichfrmige Arrays oder auch Jagged Arrays genannt.

101

Das bedeutet, dass jedes einzelne Element eines Arrays wiederum ein Array darstellt. Der nun folgende Code deklariert und initialisiert das in der Grafik gezeigte ungleichfrmige Array. Die Deklaration erkennen Sie sofort an den doppelten eckigen Klammernpaaren:

102

Sie sehen, dass ich im Editor einen Haltepunkt gesetzt habe. Sie wissen natrlich, was ich damit bezwecke. Schauen wir uns also das erstelle Array genauer an. Dazu habe ich alle Eintrge durch einen Klick auf alle Pluszeichen erweitert, so dass smtliche Inhalte sichtbar werden:

Abb. 22:

Anzeigen der Arrayinhalte

Anzahl der Array-Elemente bestimmen


Haben wir es mit einem eindimensionalen Array zu tun, so knnen wir mit Hilfe der Methode <Array>.GetLength(Dimension) die Anzahl der Elemente in der angegeben Dimension bestimmen. Die Dimensionszahl fr ein eindimensionales Array ist, wie wir schon bei der Methode GetUpperBound() gesehen haben, 0. Fr die zweite Dimension hat die Zahl den Wert 1, usw. 103

Die Konsolenausgabe lautet: 7 2 Die Gesamtsumme aller Elemente berechnet sich aus dem Produkt der Elemente der jeweilig vorhandenen Dimension. 7 x 2 = 14. Um sich diese Rechnung zu ersparen, gibt es die Eigenschaft Length. Sie ermittelt die Summe aller Elemente eines Arrays ber alle Dimensionen hinweg. Console.WriteLine(meinArray.Length); // Ergibt 14 Mchten Sie die Anzahl der Elemente eines eindimensionalen Arrays anzeigen, so liefern die Methode GetLength(0) und die Eigenschaft Length das gleiche Ergebnis.

Anzahl der Dimensionen ermitteln


Um die Anzahl der Dimensionen eines Arrays zu ermitteln, knnen wir die Eigenschaft Rank zu Rate ziehen.

104

int[,] meinArray = new int[6, 9]; Console.WriteLine(meinArray.Rank); // Ergibt 2

Arrayelemente lschen
In C# existiert eine Klasse mit Namen Array die im Namespace System definiert ist. Sie stellt Methoden fr das Erstellen, Lschen, Manipulieren, Suchen oder Sortieren zur Verfgung, von denen ich nur einige ansprechen mchte. Die Methode Clear dient zum Lschen einzelner Array-Elemente, wobei das Array selber dabei nicht gelscht wird.

Die Methode Array.Clear() setzt im Array meinArray ab dem ersten Element zwei Elemente auf den Standardwert 0 zurck.

105

Rufen Sie sich die Tatsache in Ihr Gedchtnis zurck, dass die Index-Zhlweise bei 0 beginnt und das erste Element an zweiter Position des Arrays zu finden ist. Die allgemeine Syntax der Clear-Methode lautet: Array.Clear(<Arrayname>, Startindex, Anzahl);

Array klonen
Mit der Methode Clone knnen Sie 1:1 Kopien eines Arrays erzeugen. Da der Rckgabewert vom Typ object ist, ist eine explizite Konvertierung erforderlich.

106

Zeile 14

Erklrung Deklaration und Initialisierung des Arrays nachfolgend aufgefhrten Werten.

meinArray1 vom Datentyp int mit

15

Deklaration des Arrays

das Klonen des Arrays

meinArray2 vom Datentyp int und Initialisierung ber meinArray1. Der Rckgabedatentyp der Methode

Clone object macht eine explizite Konvertierung in den Datentyp int erforderlich. 17 bis 20 Ausgabe der Elemente des geklonten Arrays

Tab. 25:

Code-Erklrungen

Array kopieren
Eine hnliche Funktionsweise bietet die Methode Copy.

107

Abb. 23:

Unterschied Clone / Copy

Im Gegensatz zur kompletten 1:1 Kopie, die mittels Clone erzielt wird, bietet die Methode Copy eine differenziertere Auswahl der zu kopierenden Elemente. Copy Version 1 Copy existiert in zwei Versionen. Die erste kopiert ab der Indexposition 0 eine bestimmte Anzahl von Elementen eines Quellarrays in ein Zielarray. Die Angabe der zu kopierenden Elemente darf die Zahl der existierenden Elemente nicht berschreiten, da es andernfalls zu einem Laufzeitfehler kommt. Ist die Zahl kleiner, werden die restlichen Elemente des Zielarrays, wie im folgenden Beispiel gezeigt, mit dem Wert 0 initialisiert. Die allgemeine Syntax lautet: Array.Copy(<Quellarray>, <Zielarray>, Anzahl);

108

Copy Version 2 Die zweite Version ist schon etwas komplexer. Sie gestattet es, anzugeben, ab welcher Position im Quellarray wie viele Elemente an welcher Stelle im Zielarray zu kopieren sind. Die allgemeine Syntax lautet: Array.Copy(<Quellarray>, Position, <Zielarray>, Position, Anzahl);

109

Wenn ich die Anweisung aus Zeile 16 in eine fr den Menschen verstndliche Form bersetzten wrde, so lautet die Order: Kopiere 2 Elemente aus dem Quellarray meinArray1 ab Index-Position 1 in das Zielarray meinArray2 ab Index-Position 2.

110

Reihenfolge eines Arrayinhaltes umkehren


Die Methode Array.Reverse() kehrt die Reihenfolge der Elemente eines Arrays um. Die Syntax lautet: Array.Reverse(<Array>);

Array sortieren

Werden Daten in ein Array berfhrt, so liegt deren Inhalt in den seltensten Fllen in sortierter Form vor. Um ein eindimensionales Array zu sortieren, knnen wir uns der Methode Array.Sort() bedienen. Da ich in dieser Einfhrung ausschlielich elementare Datentypen und keine benutzerdefinierten Klassen verwende, sind die Sortierkriterien eindeutig und der Einsatz so genannter Schnittstellen entfllt. Die allgemeine Syntax lautet: Array.Sort(<Array>);

111

Das Sortieren von Zeichenketten ist ebenso mglich, wie das von numerischen Werten aus dem letzten Beispiel.

Die Ausgabe lautet: Erde Mars Merkur Venus

112

Jeder einzelne Buchstabe der Zeichenketten wird nacheinander mit den anderen verglichen und anhand der Position im Alphabet dessen Stellenwert bestimmt. Je nher ein Buchstabe sich am Anfang des Alphabetes befindet, desto weiter vorne wird er einsortiert. Befinden sich an der zu berprfenden Position zwei gleiche Buchstaben, werden die nachfolgenden Buchstaben der gleichen Prfung unterzogen. Existieren z.B. zwei Zeichenketten, von denen die eine ac und die andere aa lautet, so haben wir eine Buchstabenbereinstimmung durch das a an der ersten Position. Beide Zeichenketten liefern sich zu diesem Zeitpunkt ein Kopf an Kopf Rennen. Der nchste Vergleich der darauf folgenden Position bringt vielleicht den entscheidenden Vorsprung. Die erste Zeichenkette kann ein c vorweisen, doch die zweite trumpft mit einem a auf. Das bedeutet den Sieg und der erste Platz ist ihr sicher. Nach diesem Prinzip werden Zeichenketten bei einem Sortiervorgang einem Vergleich unterzogen.

Operatoren
Endlich kommen wir zu einem Kapitel, das uns zeigt, was wir mit den Daten anstellen knnen, die in den Variablen gespeichert wurden. Die Programmiersprache C# stellt uns eine Reihe von Operatoren zur Verfgung, die aufgrund ihrer unterschiedlichen Funktionsweisen in Kategorien unterteilt sind. Durch einen Operator teilen wir dem Compiler mit, auf welche Weise mit den Daten verfahren werden soll.

113

Die unterschiedlichen Kategorien sind: Arithmetische Operatoren Inkrement- und Dekrement-Operatoren Vergleichsoperatoren Logische Operatoren Bitweise Operatoren Zuweisungsoperatoren Sonstige Operatoren

114

Arithmetische Operatoren
Zum Einstieg stelle ich Ihnen die Arithmetischen Operatoren vor, die eigentlich keine Probleme bereiten sollten. Es geht dabei um die Addition, Subtraktion, Multiplikation, Division und Modulo-Division:
Operator + Bedeutung Addition: Summe = Summand + Summand z.B: Summe = 5 + 9 = 14 Wird der Datentyp string verwendet, knnen mit dem + Operator Zeichenketten verbunden werden. Subtraktion: Differenz = Minuend Subtrahend z.B: Differenz = 81 62 = 19 * Multiplikation: Produkt = Faktor * Faktor z.B: Produkt = 3 * 6 = 18 / Division: Quotient = Dividend / Divisor z.B: Quotient = 9 / 3 = 3 % Modulo (Restwertoperator) z.B: 17%3 = 2 (17 dividiert durch 3 = 5 Rest 2)

Tab. 26:

Arithmetische Operatoren

Der folgende Source-Code fhrt die 5 Operationen beispielhaft aus: 115

Die Ausgabe lautet: Ergebnis: 8 Ergebnis: C# ist toll! Ergebnis: 2 Ergebnis: 15 Ergebnis: 1 Ergebnis: 2 Interessant ist natrlich Zeile 24, die eine Division zweier Integer-Werte durchfhrt und das Ergebnis einer Variablen vom Datentyp int zuweist. Bemhen wir unseren Taschenrechner, so lautet das Ergebnis der Rechnung 5/3 = 1.666666667. Durch die Zuweisung wird der Nachkommaanteil abgeschnitten. Es wird nicht gerundet! 116

Dotty: Ich hab da eine Idee, damit das Ergebnis korrekt angezeigt wird. Wir mssen doch einfach nur den Datentyp der Ergebnisvariablen von int auf z.B. float ndern. Dieser Datentyp ist in der Lage Dezimalzahlen aufzunehmen. Keine schlechte Idee. Du meinst also, wir sollten die Berechnung fr dieses eine Problem z.B. folgendermaen anpassen:

Das Ergebnis lautet leider Gottes 1 und nicht 1.666666667. Wie kommt es zu diesem Ergebnis? Wir erinnern uns: Ganzzahl Literale werden als Datentyp int angesehen. Die Division der beiden Werte findet im Speicher statt und das Ergebnis ist ebenfalls vom Datentyp int. Dieser ist aber nicht in der Lage, Dezimalzahlen zu speichern und das hat zur Folge, dass die Nachkommastellen einfach unter den Tisch fallen. Anschlieend erfolgt die Zuweisung an die Variable vom Datentyp float. Sie kann nur das Aufnehmen, was ihr vom Ergebnis der Operation zugewiesen wird. Und das ist nun mal der Wert 1. Einen Weg aus dem Dilemma ist, eine der beiden Literale zu einer Dezimalzahl zu machen. Aus 5 wird 5.0 und damit sollte die Berechnung ordnungsgem von statten gehen. Doch leider kommen wir vom Regen in die Traufe. Ein Gleitkomma-Literal ist, wie wir schon kennen gelernt haben, immer vom Datentyp double und demnach eine implizite Konvertierung in den Datentyp float unzulssig. Dotty: Es gibt doch die Typqualifizierer fr Gleitkomma-Datentypen. Machen wir aus 5.0 doch einfach 5.0F. 117

Das ist genau die Lsung des Problems. Ersetzen wir die Anweisung in Zeile 12 durch die folgende: float fltWert1 = 5.0F / 3; und das Ergebnis sollte korrekt sein.

Inkrement- und Dekrement-Operatoren


Es gibt sehr viele Einsatzgebiete in der Programmierung, in denen z.B. die Anzahl von Schleifendurchlufen gezhlt werden, um darauf entsprechend reagieren zu knnen. Zu diesem Zweck wird eine Variable deklariert, die nach und nach bei jedem Durchlauf um den Wert 1 erhht wird. Nehmen wir an, wir nennen diese Variable i und initialisieren sie mit dem Wert 0. Der Einfachheit halber sollte sie einem ganzzahligen Datentyp z.B. int angehren. Die Anweisung dazu knnte folgendermaen lauten: int i = 0; Um den Wert um 1 zu erhhen, knnen wir die Anweisung i = i + 1; benutzen. Fr diesen Zweck existiert in C#, wie auch in C oder C++, eine Kurzform dieser Schreibweise. Die Zeile i++; hat den gleichen Effekt. Analog zur Erhhung (Inkrementierung) um den Wert 1 gibt es die Verminderung (Dekrementierung) um den Wert 1, die mit der folgenden Anweisung erreicht wird: i--; Sehen Sie sich jetzt folgende Anweisung genauer an. Diesmal steht die Inkrement-Anweisung nicht alleine in einer Zeile, sondern ist Bestandteil eines Ausdrucks.

118

Die Ausgabe an die Konsole lautet 29 Dotty: Hat die Erhhung um den Wert 1 nicht stattgefunden? Die Ausgabe sollte meines Wissen 30 lauten! Die Erhhung hat definitiv stattgefunden. Fgen wir die Anweisung Console.WriteLine("i= {0}", i); hinter Zeile 14 ein, so werden wir sehen, dass i den Wert 30 innehat. Doch wie kommt das?
Zeile Erklrung 12 13 Deklariert eine Variable i vom Datentyp int und initialisiert sie mit dem Wert 29

intErgebnis vom Datentyp int. Die Initialisierung erfolgt jetzt von links nach rechts gesehen mit dem Wert der Variablen i, der noch 29 ist. Erst nach dieser Zuweisung erfolgt die Inkrementierung der Variablen i durch den Operator
++.

Deklariert eine Variable

Tab. 27:

Code-Erluterungen

Wie schaffen wir es mit Hilfe des Inkrement-Operators das gewnschte Ergebnis zu erzielen? Der Programmierer kann dies durch die entsprechende Notation zum Ausdruck bringen. Wir unterscheiden dabei Prfix-Notation Postfix-Notation 119

Das vorgestellt Beispiel fllt in die Kategorie Postfix-Notation. Der Operator wird in diesem Fall nach der Auswertung des Ausdrucks ausgefhrt. Soll die Ausfhrung vor der Auswertung erfolgen, muss eine Prfix-Notation angewendet werden. ndern Sie die Zeile 13 wie folgt ab int intErgebnis = ++i; // Prfix-Notation so wird der Wert der Variablen i vor der Zuweisung an die Variable intErgebnis inkrementiert. Die Ausgabe lautet dann 30 statt 29.
Operator ++ Bedeutung Inkrement-Operator fr Prfix- und Postfix-Notation Plazieren des Operators ++ vor eine Variable: Prfix-Notation (++i) Plazieren des Operators ++ hinter eine Variable: Postfix-Notation (i++) -Dekrement-Operator fr Prfix- und Postfix-Notation Plazieren des Operators -- vor eine Variable: Prfix-Notation (--i) Plazieren des Operators -- hinter eine Variable: Postfix-Notation (i--)

Tab. 28:

Inkrement- und Dekrement-Operator

Vergleichsoperatoren (Relationale Operatoren)


Bei einer Vergleichsoperation werden zwei Operanden miteinander verglichen und in der Bewertung ein Wahrheitswert in Form von true (wahr) oder false (falsch) zurckgeliefert. Sie erinnern sich, dass es einen Datentyp bool gibt, der genau diese zwei Zustnde kennt.

120

Operator x == y x != y x<y x>y x <= y x >= y

Bedeutung Liefert als Ergebnis true, wenn Operand x gleich Operand y ist Liefert als Ergebnis true, wenn Operand x ungleich Operand y ist Liefert als Ergebnis true, wenn Operand x kleiner Operand y ist Liefert als Ergebnis true, wenn Operand x grer Operand y ist Liefert als Ergebnis true, wenn Operand x kleiner oder gleich Operand y ist Liefert als Ergebnis true, wenn Operand x grer oder gleich Operand y ist

Tab. 29:

Vergleichsoperatoren

Der nun folgende Source-Code zeigt einige Beispiel fr Vergleichsoperatoren:

Die Interpretation des Source-Codes ist vielleicht auf den ersten Blick nicht gerade einfach und verstndlich. Ich meine damit speziell die Zeilen 16, 19, 22 und 25. In diesen Zeilen findet eine Zuweisung an eine Variable des Datentyps 121

bool statt. Doch direkt hinter dem Zuweisungsoperator = steht eine Variable des Datentyps int. Wie soll denn da eine Zuweisung stattfinden? Etwa durch eine Typkonvertierung von int nach bool? Doch das ist nicht mglich. Und dann steht da noch ein Vergleichsoperator mit einer weiteren Variablen. Bei genauerer Betrachtung ist die Lsung recht einfach und vielleicht sind Sie selber darauf gekommen. Vergleichoperatoren haben eine grere Prioritt als der Zuweisungsoperator. Aus diesem Grunde wird die Vergleichsoperation im ersten Schritt zuerst ausgewertet und das Ergebnis im zweiten Schritt der Variablen blnErgebnis zugewiesen. Exemplarisch fr die Vergleichsoperation in Zeile 16 lautet die Reihenfolge:

Logische Operatoren
Stellen Sie sich vor, Sie mchten sehen, ob sich der Wert der Variablen x innerhalb eines festgelegten Bereichs befindet. Ein Bereich definiert sich durch einen unteren und einen oberen Wert. Mit einer einzigen Vergleichsoperation knnen wir entweder nur den einen oder den anderen berprfen. Sehen Sie sich den folgenden Bereich an. Er erstreckt sich auf dem Zahlenstrahl von 10 bis 20.

Mit der Vergleichsoperation x >= 10 berprfen wir, ob sich die Variable x oberhalb des unteren Grenzwertes oder genau auf ihm befindet. Das ist aber nur die halbe Miete. Was ist, wenn die Variable x den Wert 21 hat. Die Bedingung x >= 10 ist immer noch erfllt und somit wahr. Jedoch befindet sich der Wert 21 auerhalb des definierten Bereichs. Eine zweite Vergleichsoperation muss berprfen, ob sich x unterhalb des oberen Grenzwert befindet oder genau auf ihm. 122

x <= 20 Wie aber lassen sich beide Vergleichsoperationen miteinander verknpfen, so dass die Erste und die Zweite in die Wahrheitsfindung mit einbezogen werden? Mit Hilfe von Logischen Operatoren knnen Sie Vergleichsoperationen zu Ausdrcken zusammenfassen. In unserem Fall mssen beide Bedingungen x>=10 bzw. x<=20 wahr sein, damit der Gesamtausdruck wahr ist. Zu diesem Zweck nutzen wir den logischen Und-Operator && und formulieren die Bedingung folgendermaen: Und-Operator (x >= 10) && (x <= 20)

Die Ausgabe der Konsole lautet: 123

Bereich: 10 - 20 Ergebnis fr x= 9: False Ergebnis fr x= 10: True Ergebnis fr x= 15: True Ergebnis fr x= 20: True Ergebnis fr x= 21: False Wundern Sie sich nicht darber, dass ich mehrere Zeilen umgebrochen habe. Leider ist das aufgrund des Platzmangels nicht anders machbar. Es zeigt Ihnen auf diese Weise, dass es dem Compiler vollkommen egal ist, ob Sie optisch ansprechend eingerckt haben oder sich an keine Konvention halten. Denken Sie nur daran, dass ein Zeilenumbruch innerhalb einer Zeichenkette auf diese Weise nicht mglich ist. Oder-Operator Verknpft der Oder-Operator zwei Einzelausdrcke, so ist der Gesamtausdruck wahr, wenn einer der beiden Einzelausdrcke wahr ist.

Die Konsolenausgabe lautet: Ergebnis: True In diesem Programmbeispiel existieren zwei Einzelausdrcke: (x > 5) 124

(y == 4) Der erste Ausdruck ist wahr, da die Variable x mit dem Wert 10 initialisiert wurde und 10 bekanntlich grer als 5 ist. Die Bedingung fr Gesamtausdruck ist hiermit schon erfllt. Einer der beiden Einzelausdrcke (x > 5) ist wahr. Die Prfung des zweiten Einzelausdrucks (y == 4) entfllt. Er ist zwar falsch, doch das spielt in der Bewertung, wie schon gesagt, keine Rolle mehr. Exclusiv-Oder Werden zwei Einzelausdrcke mit dem Exclusiv-Oder-Operator verknpft, ist der Gesamtausdruck wahr, wenn beide Einzelausdrcke unterschiedliche Wahrheitswerte besitzen.

Die Konsolenausgabe lautet: Ergebnis: True In diesem Programmbeispiel existieren zwei Einzelausdrcke: (x > 10) (y == 20) Der erste Ausdruck ist falsch, da die Variable x mit dem Wert 10 initialisiert wurde und 10 nicht grer 10 sein kann. Damit der Gesamtausdruck als wahr gilt, muss die nchste Einzelbedingung den Wahrheitswert wahr liefern, um der Bedingung des Exclusiv-Oders gerecht zu werden. Dem ist auch so, da y mit dem Wert 20 initialisiert wurde und 20 gleich 20 ist.

125

Logische Negation Die Logische Negation kehrt den Wahrheitswert um. Aus wahr wird falsch und umgekehrt.

Die Konsolenausgabe lautet: Ergebnis: False


Operator && || ^ ! Bedeutung Logische Und-Verknpfung Logische Oder-Verknpfung Exclusiv-Oder-Verknpfung Logische Negation

Tab. 30:

Logische Operatoren

Damit Sie ein besseres Verstndnis von logischen Operationen bekommen, schauen Sie sich die folgenden Wahrheitstabellen der einzelnen Operatoren an:
Operand 1 false false true true Operand 2 false true false true Ergebnis false false false true

126

Tab. 31:

Und-Operator &&

Operand 1 false false true true

Operand 2 false true false true

Ergebnis false true true true

Tab. 32:

Oder-Operator ||

Operand 1 false false true true

Operand 2 false true false true

Ergebnis false true true false

Tab. 33:

Exclusiv-Oder-Operator ^

Bitweise Operatoren
Da der Computer alle Daten in Form von Bits und Bytes (8 Bit) speichert, gibt es in C# die Bitweise Operatoren, die eine Manipulation der Operanden auf dieser Ebene durchfhren. Folgende Operatoren stehen zur Verfgung:
Operator & | ^ Bedeutung Bitweise Und-Verknpfung Bitweise Oder-Verknpfung Bitweise Exclusiv-Oder-Verknpfung

127

~ >>

Bitweise Negation (Einerkomplement) Bitweise Rechtsschieben. x

>> y besagt, dass alle Bits von x um y Stellen << y besagt, dass alle Bits von x um y Stellen

nach rechts verschoben werden. << Bitweise Linksschieben. x

nach links verschoben werden.

Tab. 34:

Bitweise Operatoren

Bitweise Und-Verknpfung Schauen wir uns einmal die Bitweise Und-Verknpfung der Zahl 1179 mit der Zahl 281 auf unterster Speicherebene an. Beide sind vom Datentyp short (System.Int16), der eine Datenbreite von 16 Bit aufweist.

Der Ergebniswert 25 hat nur an den Binrstellen eine 1, an denen die beiden Operanden zusammen ebenfalls eine 1 besitzen (Grau hinterlegt). Alle anderen Stellen werden mit Nullen besetzt. Schauen Sie sich dazu den folgenden Source-Code an:

128

Dotty: Das leuchtet mir alles ein. Doch warum ist eine explizite Konvertierung in den Datentyp short in Zeile 14 notwendig? Wir haben zwei Variablen des Datentyps short und der Ergebnistyp ist ebenfalls vom gleichen Datentyp. Lassen wir doch einfach mal das Casting weg und starten das Programm. Die Fehlermeldung besagt, dass der Typ int nicht implizit in short konvertiert werden kann. Doch wie kommt das zustande? Der Compiler muss also eigenmchtig irgendetwas gemacht haben, dass es zu diesem recht merkwrdigen Phnomen kommt. Wir haben zwei Ganzzahlen mit einem Operator verknpft, deren Datentyp kleiner als der von int ist. C# stellt keine Operatoren fr die folgenden Datentypen zur Verfgung: byte (System.Byte) ushort (System.UInt16) short (System.Int16) Aus diesem Grund konvertiert der Compiler den Typ von short nach int. Die Operation erfolgt demnach mit Werten vom Datentyp int und hat zur Folge, dass die Zuweisung an eine Variable des Datentyps short ohne explizite Konvertierung fehlschlgt. Bitweise Oder-Verknpfung Fr die Bitweise Oder-Verknpfung nehmen wir einfach das gleiche Zahlenbeispiel wie bei der Und-Verknpfung:

129

Der Ergebniswert 1435 hat an den Binrstellen eine 1, an denen entweder Operand1 oder Operand2 eine 1 besitzt (Grau hinterlegt). Alle anderen Stellen werden mit Nullen besetzt. Schauen Sie sich dazu den folgenden Source-Code an:

Bitweise Exclusiv-Oder-Verknpfung Mit einer Exclusiv-Oder-Verknpfung knnen entweder alle oder nur bestimmte Bits invertiert werden.

130

Es werden bei Operand1 die Bits an den Binrstellen invertiert, an den Operand2 eine 1 stehen hat (Grau hinterlegt). Alle anderen Bits von Operand1 bleiben unverndert. Schauen Sie sich dazu den folgenden Source-Code an:

Bitweise Negation (Einerkomplement) Die Bitweise Negation, auch Einerkomplement genannt, invertiert alle Bits. Aus 0 wird 1 und umgekehrt. Fr das folgende Beispiel haben wir wieder eine Variable des Datentyps short genommen.

131

Da der Datentyp short ein vorzeichenbehafteter Typ ist, wird natrlich das Vorzeichenbit an der Stelle 215 ausgewertet. Es ist von 0 nach 1 gewechselt, was bedeutet, dass die Zahl als negativ angesehen wird.

Die Konsolenausgabe lautet: -10426 Wie Sie anhand der vorliegenden Bitkombination den dezimalen Wert eines vorzeichenbehafteten Datentyps ermitteln, haben wir schon gesehen. Hier zur Vertiefung noch einmal die Schritte: 1. Auswerten des hchstwertigen Bits. Ist es 0, so ist der Wert positiv. Ist es 1, handelt es sich um eine negative Zahl. 2. Alle Bits invertieren. (Einerkomplement bilden) 3. Addieren einer 1 (Zweierkomplement bilden) ndern Sie den Datentyp von short (vorzeichenbehaftet) nach ushort (nicht vorzeichenbehaftet) und lassen das Programm der Bitweisen Negation erneut laufen, sehen Sie den Unterschied aufgrund des sich ndernden Ergebnisses. Statt -10426 erhalten wir 55110. Die Bitkombination nach der Invertierung ist unverndert geblieben, doch die Interpretation der Daten hat sich gendert.

132

Bitweise Rechtsschieben

Mit dem Schiebeoperator Bitweise Rechtsschieben knnen alle Bits eines Operanden um eine bestimmte Anzahl von Stellen nach rechts verschoben werden.

Abb. 24:

Bitweises Rechtsschieben

Dotty: Doch was passiert mit den 2 Bits auf der rechten Seite? Fr die ist irgendwie kein Platz mehr. Das ist richtig! Sie fallen sozusagen unter den Tisch und werden bei der Umrechnung nicht mehr bercksichtigt. Dotty: Wenn ich etwas nach rechts schiebe, dann wird auf der linken Seite doch Platz. Mit welchen Werten werden die jetzt entstandenen Stellen aufgefllt? 133

Handelt es sich um einen positiven Wert, werden die auf der linken Seite frei werdenden Stellen mit Nullen aufgefllt. Fr das letzte Beispiel des bitweisen Rechtsschiebens um zwei Stellen, habe ich die beiden Bits grau hinterlegt. Haben wir es dagegen mit einem negativen Wert zu tun, werden die hherwertigen Bits mit Einsen aufgefllt.

Die Konsolenausgabe lautet: 165 Bitweise Linksschieben

Mit dem Schiebeoperator Bitweise Linksschieben knnen alle Bits eines Operanden um eine bestimmte Anzahl von Stellen nach links verschoben werden. Das Prinzip ist das gleiche wie beim Rechtsschieben, nur in die entgegengesetzte Richtung. Schauen Sie sich einmal das folgende Codebeispiel an.

134

Es wird eine Zahl gespeichert, die nur das niederwertigste Bit 20 mit einer logischen 1 belegt. Dann wird dieser Wert jeweils um eine Stelle mehr nach links verschoben und das Ergebnis ausgegeben. Die Konsolenausgabe lautet: 1 2 4 8 16 Was fllt Ihnen an dieser Ausgabe auf? Ist nicht allzu schwierig, oder? Jede Ausgabe einer Zeile ist mit einer Werteverdopplung verbunden. 135

Abb. 25:

Bitweises Linksschieben

Wie wir sehen, wandert das Bit der Zahl 1 jeweils um eine Stelle nach links. Der Stellenwert einer Ziffer im Dualsystem verdoppelt sich beim Durchlaufen der Positionen vom niederwertigsten zum hchstwertigen Bit. 1,2,4,8,16 usw. Ein bitweises Linksschieben bedeutet demnach mathematisch gesehen eine Verdopplung des Wertes. Ich habe zur besseren bersicht das Beispiel recht einfach gehalten und die Schiebeoperation mit der Zahl 1 durchgefhrt. Es ist natrlich mit jedem anderen ganzzahligen Wert mglich. Probieren Sie es einfach mal aus.

Zuweisungsoperatoren
In den vorangegangenen Beispielen haben wir unzhlige male Variablen Werte zugewiesen. Ich habe dafr das einfache Gleichheitszeichen = als 136

Zuweisungsoperator verwendet, ohne Sie explizit darauf hinzuweisen. Die Syntax lautet: <Variable> = <Ausdruck>; Schauen Sie sich einmal folgende Zuweisung an:

Sie knnen mit dem einfachen Zuweisungsoperator eine Zuweisungskette erstellen, wenn mehrere Variable mit dem gleichen Wert initialisiert werden mssen.

Abb. 26:

Zuweisungskette

Operator

Bedeutung Einfache Wertzuweisung z.B. x z.B. x

= 42; Weist der Variablen x den Wert 42 zu. = y; Weist der Variablen x den Wert der Variablen y zu.

Tab. 35:

Zuweisungsoperator = 137

Zusammengesetzte Zuweisungen Kommen wir jetzt zu einer Sonderform des Zuweisungsoperators. Alle nun folgenden Zuweisungsoperatoren fallen in die Kategorie Zusammengesetzte Zuweisungen. Sie wurden geschaffen, um die Codierung diverser Zuweisungen zu verkrzen bzw. zu vereinfachen. Angenommen, Sie mchten den Wert der Variablen x um 42 erhhen. x = x + 42; Mathematisch gesehen, ist eine solche Formel natrlich der grte Humbuck. Versuchen Sie mal nach x aufzulsen. Die Anweisung ist folgendermaen zu lesen: x ist gleich dem alten Wert von x plus 42. Programmierer sind Minimalisten und versuchen Dinge zu vereinfachen und auf ein Mindestma zu reduzieren. Frage: Was ist in der letzten Anweisung zu viel? Natrlich die Angabe des zweiten x. Es dreht sich doch um die Modifikation einer einzigen Variablen. Warum sollten wir sie denn zweimal auffhren? Aus diesem Grund sind die Zusammengesetzten Zuweisungen geschaffen worden. Statt x = x + 42; schreiben wir jetzt x += 42; Die Variable x ist gleichzeitig Operand und Aufnehmer des Ergebnisses.

Sieht doch viel eleganter aus, finden Sie nicht? Die allgemeine Syntax lautet: <Variable> <Operator> = <Ausdruck>; Doch passen Sie auf, dass sie statt x += 42; nicht 138

x =+ 42; schreiben. Syntaktisch gesehen ist natrlich alles korrekt. Sie weisen der Variable x den positiven Wert +42 zu. Daran ist nichts zu beanstanden und Sie bekommen auch keine Fehlermeldung. Warum auch? Das Ergebnis, was Sie eigentlich mit dieser Anweisung bezwecken wollten, ist nicht das erwartete. Versuchen Sie spter einmal einen solchen Dreher zu finden.
Operator += Bedeutung Addition z.B. -=

x += 5; ist gleichbedeutend mit x = x + 5;

Subtraktion z.B.

x -= 4712; ist gleichbedeutend mit x = x - 4712;

*=

Multiplikation z.B.

x *= 4712; ist gleichbedeutend mit x = x - 4712;

/=

Division z.B.

x /= 4712; ist gleichbedeutend mit x = x - 4712;

%=

Modulo Division z.B.

x %= 1983; ist gleichbedeutend mit x = x % 1983;

&=

Bitweises logisches Und z.B.

x &= 8; ist gleichbedeutend mit x = x & 8;

|=

Bitweises logisches Oder z.B.

x |= 6; ist gleichbedeutend mit x = x | 8;

^=

Bitweises logisches Exklusiv-Oder z.B.

x ^= 255; ist gleichbedeutend mit x = x ^ 255;

>>=

Bitweises Rechtsschieben z.B.

x >>= 2; ist gleichbedeutend mit x = x >> 2; 139

<<=

Bitweises Linksschieben z.B.

x <<= 4; ist gleichbedeutend mit x = x << 4;

Tab. 36:

Zusammengesetzte Zuweisungsoperatoren

Sonstige Operatoren
Punktoperator . Ich mchte an dieser Stelle die restlichen Operatoren vorstellen. Einen davon, den Punktoperator (auch Zugriffs- oder Selektionsoperator), haben wir schon kennen gelernt und regen Gebrauch von ihm gemacht. Wenn Sie z.B. auf die Klassenmethode WriteLine() zugreifen, dann geben Sie die Klasse Console und ihre Methode WriteLine() durch einen Punkt getrennt an.

Der Punktoperator dient ebenfalls zur Selektion von Elementen in Namensrumen, Strukturen oder Aufzhlungen (Enumerationen). Doch dazu spter mehr. new Der Operator new dient dazu, eine Klasse zu instanziieren und somit ein Objekt zu kreieren. Alles weitere dazu finden Sie im Kapitel ber Objektorientierte Programmierung. () Sie kennen bestimmt alle noch die Regel Punktrechnung vor Strichrechnung aus dem Matheunterricht. Mit dem runden Klammern knnen Sie diese Regel auer Kraft setzten. Der Ausdruck 3 + 16 * 9;

140

ergibt 147, da 16 * 9 aufgrund der o.g. Regel zuerst berechnet wird. Erst dann wird zum Ergebnis 3 hinzuaddiert. Setzen wir jedoch runde Klammern um die ersten beiden Zahlen (3 + 16) * 9; wird 3 + 16 zuerst addiert, bevor das Ergebnis mit 9 multipliziert wird und das Endergebnis 171 lautet. Das zweite Einsatzgebiet der runden Klammern ist die Typkonvertierung mittels Casting, die wir schon kennen gelernt haben. is Der is-Operator wird verwendet, um den Datentyp eines Objektes zur Laufzeit zu bestimmen. Gerade bei Vergleichen zwischen zwei Objekten kommt er immer wieder zum Einsatz. [] Das eckige Klammerpaar wird bei Arrays verwendet, um ber den Index auf ein bestimmtes Element zuzugreifen. meinArray[17] = 42; ?: Der ?:-Operator liefert in Abhngigkeit einer Bedingung einen von zwei Werten zurck. Eine detaillierte Beschreibung finden Sie im Kapitel ber Kontrollstrukturen.
Operator . new () Bedeutung Punktoperator Wird zur Instanzierung einer Klasse verwendet Das runde Klammerpaar wird entweder zur Klammerung von Operatoren verwendet, um die herrschenden Vorrangregeln zu beeinflussen (z.B. Punktrechnung vor Strichrechnung)

x = (3 + 16 ) * 9; oder

zur Typkonvertierung mittels Casting is Wird zur Ermittlung eines Objekt Datentyps zur Laufzeit verwendet

141

[]

Das eckige Klammerpaar wird zur Adressierung eines Arrays ber dessen Index verwendet (z.B. meinArray[17])

?:

Der ternre Bedingungsoperator

Tab. 37:

Sonstige Operatoren

Rangfolge
Sie knnen nicht davon ausgehen, dass ein Ausdruck nur einen einzigen Operator besitzt. Bei komplizierten Berechnungen knnen die unterschiedlichste Kombinationen aufeinander treffen. Jetzt wird nicht nach dem Motto Wer zuerst kommt oder grundstzlich von links nach rechts oder umgekehrt verfahren. Es existiert eine definierte Rangfolge der Ausfhrung von einzelnen Operationen. Die nachfolgende Tabelle gibt Aufschluss darber:
Prioritt 1 (oberste) Kategorie Primr Operator(en) Klasse.Methode checked, unchecked 2 3 Unr Arithmetisch multiplikativ 4 Arithmetisch additiv 5 6 Verschiebung Relational und Typtest 7 8 Gleichheit Logisch ==, != &, ^, | <<, >> <, >, <=, >=, is, as +, +, -, !, ~, ++x, --x, (<Typ>)x Typkonvertierung bzw. Casting *, /, % (Punktoperator), f(x) Methodenaufruf,

meinArray[x] (indizierter Zugriff), x++, x--, new, typeof,

142

9 10

Bedingt Zuweisung

&&, ||, ?: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=

Tab. 38:

Rangfolge der Operatoren

Kontrollstrukturen
In den wenigsten Fllen kommt ein Programm ohne Ablaufsteuerung aus. Das bedeutet, dass zur Laufzeit Entscheidungen getroffen werden mssen, um auf bestimmte Umstnde entsprechend zu reagieren. Angenommen, Sie haben ein Programm geschrieben, das vom Benutzer die Eingabe eines ganzzahligen Wertes innerhalb definierter Grenzen erwartet. Sollte die Anwendung blauugig alles akzeptieren, was sie vom Benutzer geliefert bekommt? Sicher nicht, denn die Stabilitt eines Programms hngt nicht unwesentlich von so genannten Plausibilittschecks ab. Sie treffen Entscheidungen und lenken den Programmfluss in entsprechende Bahnen, die der Programmierer fr mgliche Eventualitten vorgesehen hat. Aufgrund der Komplexitt der heutigen Programme kann man die Aussage treffen, dass kein Programm ohne Fehler ist. Irgendein Zustand zur Laufzeit des Programms, den der Programmierer nicht vorhergesehen hat und fr den es keine passende Reaktion gibt, fhrt im schlimmsten Fall zum Absturz und zu einem Datenverlust. Eine Mglichkeit, den Programmfluss zu beeinflussen, sind die Kontrollstrukturen.

143

Die in ihnen enthaltenen Ausdrcke, liefern als Ergebnis entweder true fr wahr oder false fr falsch zurck.

if-Anweisung
Die if-Anweisung bewertet eine ihr bergebene Bedingung und fhrt in Abhngigkeit des Ergebnisses eine oder mehrere Anweisungen aus. Die Syntax lautet: if (<Bedingung>) // Anweisung Die Syntax fr mehrere Anweisungen in einem Anweisungsblock lautet: if (<Bedingung>) { // Anweisung(en) } 144

Abb. 27:

Flussdiagramm if-Anweisung

Ist die Bedingung wahr, wird die Anweisung ausgefhrt, die der if-Anweisung unmittelbar folgt. Bei einem folgenden Anweisungsblock, wird dieser komplett abgearbeitet. Das folgende Bild zeigt eine klassische Wenn-Dann Abfrage:

145

Das folgende Programm erwartet eine Eingabe einer Zahl innerhalb festgelegter Grenzen. Werden diese verletzt, wird entsprechend reagiert und ein Hinweis auf die Konsole ausgegeben:

Zeile 14

Bedeutung Deklarierung der Variablen

intEingabe fr die Aufnahme der

Benutzereingabe. 18 Initialisierung der Variablen

intEingabe mit dem Wert, den der Benutzer

ber die Konsole eingegeben hat. 19 if-Anweisung mit Bedingung zur Prfung der Eingabe. Liefert die Bedingung den Wert true zurck, ist der Wert also kleiner 1 oder grer 10, wird die folgende

146

Zeile

20

ausgefhrt

und

ein

entsprechender

Hinweis

ber

die

Wertebereichsverletzung ausgegeben.

Tab. 39:

Code-Erluterungen

Das Programm hat einen gravierenden Nachteil. Es wird nur bei einer Wertebereichsverletzung ein Hinweis ber die Konsole ausgegeben. Liefert die Bedingung den Wert false (falsch) zurck, passiert nichts weiter. Fr diesen Zweck kann die if-Anweisung um einen optionalen else-Zweig erweitert werden.

if-else-Anweisung
Die if-else-Anweisung wertet die Bedingung aus und fhrt bei einem Rckgabewert von true (wahr) die Anweisung(en) im if-Block aus. Lautet der Rckgabewert dagegen false (falsch), werden die Anweisungen im else-Block ausgefhrt. Die Syntax lautet: if (<Bedingung>) // Anweisung else // Anweisung Die Syntax fr mehrere Anweisungen in einem Anweisungsblock lautet: if (<Bedingung>) { // Anweisung(en) } else { 147

// Anweisung(en) }

Abb. 28:

Flussdiagramm if-else-Anweisung

Die folgende Erweiterung um den else-Zweig vervollstndigt das Programm, so dass in jedem Fall eine Auskunft ber die Eingabe erfolgt:

148

Jetzt, wo wir unser Programm um den else-Zweig erweitert haben, knnen wir diesem eine weitere Bedingung hinzufgen und so weiter.

if-else-if Konstrukt
Wir knnen mit diesem Konstrukt z.B. prfen, ob der Wert einer Variablen kleiner, grer oder gleich einer Zahl ist.

149

Zeile 15 17 19

Bedeutung if-Anweisung mit Bedingung zur Prfung, ob intWert kleiner 100 ist. else-if-Konstrukt mit Bedingung zur Prfung, ob intWert grer 100 ist. Trifft keine der o.g. Bedingungen zu, wird der else-Zweig ausgefhrt.

Tab. 40:

Code-Erluterungen

?:-Operator
Der ?:-Operator (tenrer Operator) liefert in Abhngigkeit einer Bedingung einen von zwei Werten zurck. Dotty: Was heit denn tenr? Operatoren knnen aufgrund der Anzahl ihrer Operanden in drei Kategorien unterteilt werden:
Kategorie unr binr tenr Anzahl der Operanden 1 2 3

Tab. 41:

Operator-Kategorien

Da der ?:-Operator - auch Bedingungsoperator genannt - drei Operanden (Bedingung, Wert1 und Wert2) hat, fllt er in die Kategorie tenr. Die Syntax lautet: <Variable> = <Bedingung> ? <Wert1> : <Wert2>; Der folgende Source-Code liest einen Wert ber die Konsole ein. Es muss sich dabei um einen ganzzahligen positiven oder negativen Wert handeln.

150

Die Aufgabe des Programms ist dabei, den Absolutwert (auch Betrag) einer Zahl zu ermitteln. Der Absolutwert einer Zahl ist deren Gre ohne Bercksichtigung des Vorzeichens. |-42| und |+42| ergibt 42.

Liefert die Bedingung der Wert true (wahr) zurck (die Zahl ist grer Null und demnach positiv), wird der Variablen intErgebnis der Inhalt der Variablen intEingabe zugewiesen. Sollte die Bedingung jedoch false (falsch) zurckliefern (die Zahl ist kleiner oder gleich Null), wird der Variablen intErgebnis der Inhalt der Variablen intEingabe mit umgekehrten Vorzeichen zugewiesen. Zwei negative Vorzeichen wandeln sich in ein positives. Auf diese Weise wird der Variablen intErgebnis stets ein positiver Wert zugewiesen.

151

switch-Anweisung

Die switch-Anweisung bietet eine komfortable Mglichkeit, eine oder mehrere Anweisungen in Abhngigkeit vom Wert einer Variablen auszufhren. Natrlich knnen wir fr eine derartige Aufgabe die if-Anweisung bemhen und einen Rattenschwanz an Abfragen hintereinander setzen, wie das folgende Beispiel zeigt:

152

Die Anwendung fragt den Benutzer z.B. nach der Note der letzten MatheKlausur. Sie werden bestimmt erkannt haben, dass sich die if-Abfragen ausschlielich auf die Variable intNote beziehen. Diese wiederholte Abfrage lsst sich in eine switch-Anweisung einmalig platzieren.

Die allgemeine Syntax lautet: switch (<Ausdruck>) { case <Wert1>: // Anweisung(en) 153

// Sprunganweisung case <Wert2>: // Anweisung(en) // Sprunganweisung ... [default: // Anweisung(en) // Sprunganweisung] }

154

switch(intVar)

case 1

Anweisung(en)

case 2

Anweisung(en)

default

Anweisung(en)

Abb. 29:

Flussdiagramm switch-Anweisung

Schauen wir uns die Arbeitsweise der switch-Anweisung etwas genauer an. Damit switch korrekt arbeitet, muss der eingesetzte Ausdruck entweder eine Ganzzahl oder eine Zeichenkette liefern. Jetzt wird geprft, ob der Wert des Ausdrucks mit einem hinter den case-Anweisungen aufgefhrten Konstanten bereinstimmt. Dabei werden alle case-Anweisungen von oben nach unten diesem Vergleich unterzogen und im Falle einer bereinstimmung die Anweisung(en) des entsprechenden case bis zum Erreichen der 155

Sprunganweisung ausgefhrt. Eine genannte Konstante darf sich innerhalb einer switch-Anweisung nicht wiederholen.

break-Anweisung Ersetzten Sie die Sprunganweisung durch eine break-Anweisung, bewirkt dies beim Erreichen ein Verlassen des kompletten switch-Blocks und der Fortfhrung des Programms mit der darauf folgenden Anweisung.

Fhrt der Vergleich zu keiner bereinstimmung, wird - falls vorhanden - zur optionalen default-Anweisung verzweigt. Eine Sprunganweisung ist immer dann zwingend vorgeschrieben, wenn hinter einem case eine oder mehrere Anweisungen stehen. Jedoch keine Regel ohne Ausnahme. 156

Dotty: Wie realisiere ich eigentlich ein Vorhaben, in dem bei mehreren aufeinander folgenden Werten, z.B. ein Bereich von 4 bis 7, eine Anweisung ausgefhrt wird? Leider funktioniert das nicht so einfach und intuitiv, wie z.B. bei Visual Basic.NET. (Case 4 To 7). Falls einige von Ihnen auf die Idee kommen sollten, den gewnschten Bereich folgendermaen zu kodieren case 4 - 7: erleiden Sie leider Schiffbruch. Die Kompilierung bereitet keine Probleme und alles scheint in Ordnung zu sein. Jedoch reagiert die Anwendung nicht wie gewnscht. Der Ausdruck 4 - 7 wird berechnet und das Ergebnis -3 als Konstante interpretiert. Doch nun zur Lsung des Problems. Setzen Sie einfach mehrere case-Anweisungen ohne break-Anweisungen hintereinander.

Werden mehrere case-Anweisungen ohne Code kaskadierend hintereinander gesetzt, wirkt das wie ein einziges case fr mehrere Werte. Es wird von einem zum nchsten gesprungen. Sicherlich ist die Kodierung fr einen angenommenen Wertebereich von 1 bis 100 recht mhsam und wir mssen uns die berechtigte Frage stellen, ob sich der Aufwand lohnt, da die Realisierung mittels if-Anweisungen fr diesen Fall doch effizienter und bersichtlicher wre.

157

goto-Anweisung Die zweite mgliche Sprunganweisung kann durch eine goto-Anweisung erfolgen.

Die Pfeile markieren den Programmverlauf innerhalb der switch-Anweisung. Die Variable a ist mit dem Wert 1 initialisiert worden, was zur Folge hat, dass die erste case-Anweisung die Kontrolle bernimmt. Die nachfolgende Anweisung Console.WriteLine("1"); wird ausgefhrt. Jetzt wird die goto-Anweisung in Zeile 19 erreicht. Sie hat als Zieladresse die case 2 Anweisung, was einen unmittelbaren einen Sprung in Zeile 21 bewirkt . Die Ausfhrung des Programms wird mit der Anweisung Console.WriteLine("2"); in Zeile 22 fortgesetzt. Nach dem Erreichen der break-Anweisung in Zeile 23 wird der switch-Block verlassen und mit der darauf folgenden Anweisung in Zeile 31 fortgefahren. 158

Die Konsolen-Ausgabe lautet: 1 2

goto-Anweisung (auerhalb von switch)


In Zeiten frher Basicprogrammierung war es sehr beliebt, mittels gotoAnweisungen durch den Programmcode zu springen. Lassen Sie einfach mal die folgende Grafik auf sich wirken und stellen Sie sich die Frage, ob das in irgendeiner Art und Weise strukturiert aussieht.

Falls Sie schon jemals den Begriff Spaghetti-Code gehrt haben, kennen Sie jetzt dessen Ursprung. Diese unbedingten Sprnge fhren dazu, dass der Code schwer zu lesen und noch schwerer zu debuggen ist. In einer hheren Programmiersprache, wie C# eine ist, sollte goto nach Mglichkeit keine Verwendung finden und nur in Ausnahmefllen, wie z.B. als Notausstieg aus mehrfach verschachtelten Schleifen, eingesetzt werden. Frher wurden in Basic die Anweisungen in Zeilen mit Zeilennummern kodiert. Wollte man zu einer bestimmten Anweisung springen, gab man die entsprechende Zeilennummer dem goto als Argument mit. In C# suchen Sie aber vergeblich nach Zeilennummern (Die im Editor dienen nur als Untersttzung bei der 159

Programmierung und als Debug-Hilfe). Die Frage, die wir uns stellen ist: Wie knnen wir eine bestimmte Stelle im Source-Code anspringen? So ohne weiteres geht es nicht. Wir mssen einen Zielpunkt, besser gesagt, ein Label (Marke) definieren, zu der die goto-Anweisung springen kann.

Die Konsole-Ausgabe lautet: Hallo User, C# macht wirklich Spa! Die Anweisung in Zeile 17 wird durch die goto-Anweisung elegant bersprungen und die Ausfhrung in Zeile 19 fortgesetzt. Das Label muss einen eindeutigen Namen und als Abschluss einen Doppelpunkt besitzen. Die Syntax lautet: <Labelname>: Ich denke, dass Sie mittlerweile aufmerksame Beobachter geworden sind und die Schlangenlinien unterhalb von Console in Zeile 17 bemerkt haben. Die Entwicklungsumgebung ist so intelligent, dass sie Codezeilen aufsprt, die zu keiner Zeit whrend der Ausfhrung erreicht werden. Der Blick in die Fehlerliste zeigt uns:

160

Nachfolgend eine Liste mit Fehler- bzw. Warnhinweisen, die in Verbindung mit goto auftreten knnen:
Fehler- bzw. Warnmeldung Die Bezeichnung <LABEL> ist im Bereich der goto-Anweisung nicht vorhanden. Unerreichbarer Code wurde entdeckt. Bedeutung Das hinter der goto-Anweisung genannte Label ist nicht vorhanden. Es wurden Codezeilen entdeckt, die aufgrund einer goto-Anweisung zu keiner Zeit ausgefhrt werden. Auf diese Bezeichnung wurde nicht Es wurde ein Label definiert, auf welches jedoch keine goto-Anweisung zeigt.

verwiesen.

Tab. 42:

Fehler- bzw. Warnmeldungen

Schleifen
Es existieren in der Programmierung unzhlige Situationen, in denen Befehle nicht nur einmal, sondern mehrfach ausgefhrt werden mssen. Zu diesem Zweck wurden so genannte Schleifen (engl. Loops) entwickelt. Sie bieten die Mglichkeit, Anweisungen wiederholt auszufhren. Es kann sich dabei um eine einzelne Anweisung handeln oder um einen Anweisungsblock. Damit eine Schleife nicht endlos weiter luft, wird eine Bedingung definiert, welche zur Schleifensteuerung herangezogen wird.

for-Schleife
Die for-Schleife ist wohl die bekannteste ihrer Art. Sie kommt immer dann zum Einsatz, wenn von vorne herein klar ist, wie oft sie zu durchlaufen ist. Ein vorzeitiges Verlassen der Schleife ist dennoch mglich. Siehe breakAnweisung. Die Syntax fr die Wiederholung einer einzelnen Anweisung lautet: 161

for (<Initialisierung>;<Bedingung>;<Reinitialisierung>) // Anweisung

Die Syntax fr die Wiederholung mehrerer Anweisungen lautet:

for (<Initialisierung>;<Bedingung>;<Reinitialisierung>) { // Anweisung(en) }

162

Laufvariable initialisieren

Abb. 30:

Flussdiagramm der for-Schleife

Wie schon eingangs erwhnt, muss ein Mechanismus existieren, der berwacht, wie of die Schleife noch zu durchlaufen ist, bzw. wann sie zu verlassen ist. Deshalb wird zu Beginn eine Laufvariable, die als Zhler dient, mit einem Wert initialisiert. Es ist der Startwert, mit dem die Schleife in den ersten Durchlauf geht. So lange die definierte Bedingung den Wert true (wahr) zurckliefert, wird die Schleife durchlaufen. Um eine Endlosschleife zu vermeiden, darf unter keinen Umstnden das ndern des Wertes der Laufvariablen vergessen werden, damit zu irgendeinem spteren Zeitpunkt die Bedingung den Rckgabewert 163

false (falsch) liefert, was den Abbruch der Schleife bedeutet. Das folgende Beispiel schreibt die Werte von 1 bis 5 auf die Konsole:

Konsolen-Ausgabe: Wert von i= 1 Wert von i= 2 Wert von i= 3 Wert von i= 4 Wert von i= 5 Initialisierung Wie wir in Zeile 15 sehen, wird eine Laufvariable mit Namen i verwendet, die innerhalb der Schleife deklariert und mit dem Wert 1 initialisiert wird. Das bedeutet, dass sie nur innerhalb der for-Schleife existent ist. Wir sprechen von einem Gltigkeitsbereich der Variablen, der auf die for-Schleife begrenzt ist. Die Deklaration von i kann ebenfalls auerhalb der Schleife erfolgen. Auf sie kann auch nach dem Verlassen der Schleife innerhalb der Methode Main() noch zugegriffen werden.

164

Da wir nur eine einzige Anweisung innerhalb der Schleife zur Ausfhrung bringen mchten, wird kein Anweisungsblock bentigt. Die Zeile, die der Schleife unmittelbar folgt, wird von ihr kontrolliert bzw. ausgefhrt. Um die Zusammengehrigkeit optisch hervorzuheben, rckt der Editor die Anweisung in Zeile 16 entsprechend ein. Der folgende Code mit Anweisungsblock ist ebenso mglich, jedoch nicht unbedingt notwendig.

Fr mich persnlich ist es trotz einer einzigen Anweisung bersichtlicher, wenn ein Anweisungsblock verwendet wird. Fr welche Version Sie sich entscheiden, bleibt Ihnen allein berlassen und hat keine Auswirkung auf die Funktionsweise des Codes. Bedingung Vor jedem Schleifendurchlauf wird der Wahrheitsgehalt der Bedingung berprft, die als Kontrollinstanz fungiert. Sie entscheidet, ob der nchste Durchlauf gestartet werden kann. Liefert die Bedingung den Wahrheitswert true (wahr) zurck, kanns in die nchste Runde gehen. Beim ersten Start hat die Laufvariable i den Wert 1. Die Frage, die sich die Kontrollinstanz nun stellt, lautet: Ist der Wert von i kleiner oder gleich 10? Dem ist so, denn 1 ist kleiner gleich 10. Die Schleife wird durchlaufen. Liefert die Bedingung gleich zu Beginn der for-Schleife den Wert false (falsch) zurck, wird die Schleife berhaupt nicht ausgefhrt, wie das folgende Beispiel zeigt.

165

Reinitialisierung Wurde das Ende eines Schleifendurchlaufs erreicht, wird die Anweisung i++ ausgefhrt. Sie ist dafr zustndig, die Laufvariable i jeweils um den Wert 1 zu erhhen. Anschlieend geht es zurck an den Anfang der Schleife, um die Bedingung erneut zu prfen. Fr Laufvariablen verwendet man hufig die Bezeichnungen i, j, k usw. Es ist unblich, ihnen ein Prfix, welches Aufschluss ber den Datentyp gibt, voranzustellen. Kurzes Anwendungsbeispiel Die folgende Quick & Dirty Anwendung (Anwendung ohne jegliche Sicherheitsmechanismen bezglich fehlerhafter Eingaben) erfragt vom Benutzer einen ganzzahligen Wert ber die Konsole. Die Anzahl der ausgegeben #Zeichen entspricht dem, des eingegebenen Wertes.

166

Break Im Schleifenkopf einer for-Schleife wird exakt festgelegt, wie oft ein Durchlauf stattfinden soll. Die break-Anweisung ermglicht es uns, aus dieser Umarmung auszubrechen. Kommt break zur Ausfhrung, wird die Schleife augenblicklich verlassen und die Programmausfhrung an der Stelle fortgefhrt, die der Schleife unmittelbar folgt.

Der Anweisungsblock der for-Schleife endet in Zeile 20. Hat der Wert der Laufvariablen i den Wer 5 erreicht, wird die break-Anweisung ausgefhrt und die Schleife verlassen. Die Fortsetzung des Programms erfolgt demnach mit Zeile 21. Konsolen-Ausgabe: Wert: 1 167

Wert: 2 Wert: 3 Wert: 4 Wert: 5 Provozieren einer Endlosschleife Der folgende Source-Code provoziert eine Endlosschleife, die jedoch mit einer break-Anweisung aufgefangen wird.

Werden im Schleifenkopf einer for-Schleife die optionalen Ausdrcke fr Initialisierung, Bedingung und Reinitialisierung weggelassen, wird eine Endlosschleife programmiert. Um aus ihr auszubrechen, ist auf jeden Fall eine break-Anweisung notwendig. Continue Stellen Sie sich vor, Sie mssten ein Programm schreiben, das nur ungerade Zahlen zwischen 0 und 10 an die Konsole ausgibt.

168

In Zeile 17 findet eine berprfung statt, ob die Laufvariable i ohne Rest durch 2 teilbar ist. Wenn dem so ist, wird die Schleife nicht verlassen, jedoch der nchste Schleifendurchlauf erzwungen. Das hat zur Folge, dass bei geraden Zahlenwerten von i, die Ausgabe an die Konsole in Zeile 19 nie erreicht wird. Konsolen-Ausgabe: Wert: 1 Wert: 3 Wert: 5 Wert: 7 Wert: 9 Natrlich soll das nur ein Beispiel fr die Verwendung von continue sein. Es gibt sicherlich noch andere Lsungsanstze, die gleiche Ausgabe zu erreichen. Mehrere Laufvariable Der Einsatz einer for-Schleife ist nicht auf eine einzige Laufvariable begrenzt. Sie knnen, falls notwendig, zwei oder mehr Variablen und die dazugehrigen Inkrementierungs- bzw. Dekrementierungsanweisungen, durch Kommata getrennt, angeben.

169

Konsolen-Ausgabe: i= 1, j= 5 i= 2, j= 4 i= 3, j= 3 i= 4, j= 2 i= 5, j= 1 Hilfe, eine Endlosschleife Trotz aller Vorsicht kann es manchmal vorkommen, dass sich Ihr Programm in einer Endlosschleife festgebissen hat. Sie brauchen jetzt Ihre Entwicklungsumgebung nicht zu verlassen oder etwa den Rechner zu rebooten. ber den Menpunkt Ansicht|Symbolleisten|Debuggen erhalten Sie Zugriff auf die Debug-Kontrolle. Klicken Sie dort auf den quadratischen Stop-Button. Die Bedienung hnelt ein wenig an die Steuerung eines DVD-Players mit Start, Pause und Stop.

Alternativ knnen Sie das Programm auch mit der Tastenkombination Strg + C oder Strg + Pause beenden.

170

foreach-Schleife
Rufen Sie sich das Kapitel ber die Arrays in Ihr Gedchtnis zurck. Die Klasse Array hat u.a. eine Methode mit Namen GetUpperBound(). Sie dient dazu, die obere Arraygrenze zu ermitteln. Im folgenden Programmbeispiel nutzen wir diese Methode, um der for-Schleife mitzuteilen, wie weit sie zu zhlen hat, ohne dabei die obere Arraygrenze zu verletzten.

In C# existiert eine abgewandelte for-Schleife, die alle Elemente eines Arrays oder einer Liste anspricht. Sie lautet foreach, was bersetzt fr jedes heit. Genau das ist die Aufgabe dieser Schleife. Sie soll jedes Element beim Schleifendurchlauf ansprechen, ohne dass dafr eine explizite Angabe ber die Anzahl der Elemente gemacht werden muss. Die Syntax lautet: foreach ([<Datentyp>] <Variable> in <Array>) { // Anweisung(en) } Der folgende Source-Code fhrt zum gleichen Ergebnis, wie beim Einsatz mit der Methode GetUpperBound() im letzten Beispiel.

171

Alle im Array meinArray enthaltenen Elemente werden durch die foreachSchleife nach und nach angesprochen und an die Laufvariable intElement bergeben.

Bei jedem Schleifendurchlauf zeigt die Variable intElement, quasi als Zeiger, auf das nchste Element des Arrays meinArray. Whrend der Zuweisung findet mglicherweise eine Typkonvertierung statt, bei der der Datentyp der Arrayelemente in den der Laufvariablen umgewandelt wird.

while-Schleife
Im Gegensatz zu einer for-Schleife, bei der die Randbedingungen bezglich der Anzahl der Schleifendurchlufe mehr oder weniger feststehen und ber die Laufvariable im Schleifenkopf kontrolliert wird, bergeben wir der whileSchleife eine Bedingung. Solange diese den Wahrheitswert true (wahr) zurckliefert, wird die Schleife durchlaufen. Die logische Konsequenz daraus ist, dass sich innerhalb des Schleifenkrpers eine oder mehrere Anweisungen 172

befinden mssen, die den Wahrheitswert der Bedingung beeinflussen. Andernfalls provozieren Sie eine Endlosschleife.

Die while-Schleife ist eine so genannte kopfgesteuerte Schleife. Die berprfung, ob ein Schleifendurchlauf erfolgen soll, findet im Kopf der Schleife statt. Das kann natrlich dazu fhren, dass die Bedingung gleich zu Anfang nicht erfllt wird und der Schleifendurchlauf berhaupt nicht stattfindet. Die Syntax lautet: while (<Bedingung>) { // Anweisung(en) }

173

Abb. 31:

Flussdiagramm while-Schleife

Vergleichbar mit der Ausgabe der Zahlen von 1 bis 10 mittels for-Schleife, kann das gleiche Ergebnis ber die while-Schleife erzielt werden. 174

Endlosschleife Das Programmieren einer Endlosschleife mit while ist recht einfach. Wir mssen dafr sorgen, dass die Bedingung stndig den Rckgabewert true (wahr) liefert. Was hindert uns daran, direkt true an die Stelle der Bedingung zu setzen? while ( true ) { // Anweisung(en) // break nicht vergessen! } Break Eine Schleifenunterbrechung ist auch hier ber die break-Anweisung mglich. Es wird mit der Anweisung fortgefahren, die der Schleife unmittelbar folgt. Continue Bei der continue-Anweisung besteht ein kleiner, aber nicht unerheblicher Unterschied im Gegensatz zur Anwendung innerhalb einer for-Schleife. Kommt sie innerhalb einer for-Schleife zum Einsatz, so bedeutet das einen erneuten Schleifendurchlauf, bei der der Schleifenkopf, insbesondere die Inkrementierung der Laufvariablen eine bedeutende Rolle spielt. Die Laufvariable wird dort gendert und anschlieend die Bedingung ausgewertet. Fhren wir die continue-Anweisung innerhalb von while aus, so erfolgt keine implizite Anpassung der Laufvariablen. Eine Endlosschleife wre das Ergebnis.

175

Der vorliegende Source-Code gibt wieder alle ungeraden Zahlen zwischen 1 und 10 aus. Die berprfung findet in Zeile 18 statt. Wichtig ist hierbei die nderung der Laufvariablen i in Zeile 20. Ungewollte Endlosschleife durch Semikolon Gerade Anfngern kann der folgende Fehler unterlaufen. Wir haben zu Beginn gelernt, dass jede Anweisung in C# mit einem Semikolon abgeschlossen werden muss. Setzten wir dagegen ein einzelnes Semikolon, wird dieses als eine Leeranweisung interpretiert.

176

Zeile 14 15

Bedeutung Die Variable a wird deklariert und mit dem Wert 10 initialisiert. Die Bedingung der while-Schleife berprft, ob der Wert von a grer 10 ist. Da der while-Schleife ein Semikolon folgt, wird dieses als (Leer)Anweisung angesehen, die die Schleife im Falle der Bedingungserfllung ausfhrt. a ist grer 0 und die Bedingung gilt als erfllt. Es geht in die nchste Schleifenrunde. Sie erkennen, dass die Variable a innerhalb der Bedingung nie eine nderung erfhrt und wir damit eine klassische Endlosschleife produziert haben. Die geschweiften Klammern, die der while-Schleife folgen, werden vom Compiler nicht als Fehler erkannt. Sie kennzeichnen einen Anweisungsblock. Zeile 19, die eigentlich fr die nderung der Bedingungsvariablen a kodiert wurde, wird nie ausgefhrt.

Tab. 43:

Code-Erluterungen

Vielleicht sind Ihnen die Schlangenlinien unterhalb des Semikolons in Zeile 15 aufgefallen. Es ist ein netter Hinweis der Entwicklungsumgebung an uns, dass mglicherweise etwas nicht stimmt. Wenn Sie mit der Maus darber fahren oder einen Blick in die Fehlerliste werfen, sehen Sie den Grund.

177

Einer while-Anweisung darf kein Semikolon angefgt werden, da dies zu einer Endlosschleife fhrt, falls die Bedingung true (wahr) zurckliefert.

do-Schleife
Die do-Schleife verhlt sich ein wenig anders. Da die Bedingung am Ende der Schleife definiert ist, findet keine berprfung beim Eintritt in die Schleife wie bei for und while statt. Das hat zur Folge, dass mindestens ein mal ein Durchlauf stattfindet. Erst am Fu der Schleife wird die Bedingung ausgewertet und ggf. ein Abbruch erwirkt. Diesem Umstand zufolge wird sie auch fugesteuerte Schleife genannt. Die Schleife wird solange durchlaufen, wie die definierte Bedingung den Wahrheitswert true (wahr) zurckliefert.

Die Syntax lautet: do { // Anweisung(en) } while (<Bedingung>);

178

Abb. 32:

Flussdiagramm do-Schleife

Vergessen Sie nicht das Semikolon am Ende der Schleifendefinition. Es wird leicht bersehen, da es bei einer while-Schleife nicht bentigt wird. Das folgende Programm simuliert ein Ratespiel, bei dem es darum geht, dass der Benutzer eine Zahl zwischen 0 und 10 erraten soll. Er bekommt nach der Eingabe seiner Zahl einen Hinweis, ob die Zahl zu klein, zu gro oder richtig ist. Ebenso werden die Anzahl der Versuche protokolliert. Das Beispiel verdeutlicht uns, dass es auf jeden Fall sinnvoll ist, mindestens einmal die Schleife zu durchlaufen. Fr dieses Programmbeispiel mache ich wieder eine Anleihe aus einem Themenbereich, den wir noch nicht besprochen haben. Es geht dabei um die Erzeugung von Zufallszahlen. 179

Zeile 14 und 15

Bedeutung Generierung einer Zufallszahl zwischen 0 und 10 und Zuweisung an die Variable

intZufallszahl.
16 Deklaration der Variablen intEingabe fr die Aufnahme der vom Benutzer gemachten Zahl. Deklaration und Initialisierung der Variablen

intVersuche, die die Anzahl der Versuche enthlt.


20 22 24 Beginn der do-Schleife Inkrementierung der Variablen intVersuche bei jedem Schleifendurchlauf Konsoleneingabe in die Variable

intEingabe speichern. Eine

180

Konvertierung in den Datentyp int ist notwendig, da der Rckgabe-Datentyp der Methode ReadLine() string ist. 25 27 29 berprfung, ob die Eingabe grer der Zufallszahl ist. berprfung, ob die Eingabe kleiner der Zufallszahl ist. Wenn sie weder grer noch kleiner ist, muss sie gleich der Zufallszahl sein. Ausgabe der Anzahl der bentigten Versuche. 35 Bedingungsauswertung der do-Schleife. Solange die Eingabe des Benutzers ungleich der Zufallszahl ist, fhre die Schleife erneut aus.

Tab. 44:

Code-Erluterungen

Break Siehe while-Schleife. Continue Siehe while-Schleife.

Verschachtelung
Von einer Verschachtelung spricht man, wenn sich innerhalb einer Schleife eine weitere befindet. Das folgende Beispiel erzeugt das kleine Einmaleins auf der Konsole.

181

Zeile 15

Bedeutung Start der ueren Schleife mit der Laufvariablen i. Sie wird zu Beginn mit dem Wert 1 initialisiert.

18

Start der inneren Schleife mit der Laufvariablen j. Sie ist in die uere eingebettet und durchluft alle Wert von 1 bis 10. Nach dem Erreichen des Schleifenendes der inneren Schleife, wird der nchste Schleifendurchlauf der ueren Schleife begonnen. Die innere Schleife beginnt erneut von 1 bis 10 zu zhlen. Das Programm endet, wenn auch die uere Schleife die Werte von 1 bis 10 durchlaufen hat.

20

Durchfhrung der Multiplikation der Werte aus der ueren bzw. der inneren Schleife. Beachten Sie, dass die Variable i auch in der inneren Schleife sichtbar ist, jedoch j nicht in der ueren. Die Ausgabe wird mit der Formatanweisung {0,4} formatiert, um eine gleichfrmige Spaltenbreite von 4 Zeichen zu erreichen.

22

Einfgen einer Leerzeile in der Konsolenausgabe, wenn die innere Schleife komplett abgearbeitet wurde.

Tab. 45: 182

Code-Erluterungen

Konsolen-Ausgabe: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 10 20 30 40 50 60 70 80 90

90 100

Dotty: Die Laufvariable j der inneren Schleife wird bei jedem Durchlauf der ueren Schleife erneut deklariert bzw. initialisiert. Eigentlich sollte dies zu einem Fehler fhren. Eine Variable mit gleichem Namen mehrfach zu deklarieren ist doch nicht mglich. Die letzte Aussage ber die Mehrfachdeklaration ist korrekt. Jedoch stimmt der erste Teil nicht ganz. Wir haben schon gesehen, dass eine Variable, die innerhalb einer Schleife deklariert wurde, ihren Gltigkeitsbereich nur innerhalb dieser Schleife hat. Wird die Schleife verlassen, ist sie nicht mehr existent und kann somit beim nchsten Durchlauf der ueren Schleife erneut deklariert werden, ohne dadurch eine Verletzung der Eindeutigkeit im lokalen Bereich zu schaffen.

Zeichenketten
Zeichenketten werden in der Programmiersprache auch Strings genannt. Sie sind in der Lage ein oder mehrere Zeichen zu speichern. Ein einzelnes Zeichen 183

wird stets im Unicode-Zeichensatz gespeichert, der dafr 2 Bytes bentigt. Der Datentyp ist uns schon einige male ber den Weg gelaufen und nennt sich string und der .NET-Laufzeittyp lautet System.String. Ein String wird stets von doppelten Hochkommata eingeschlossen:

Die Lnge eines Strings wird eigentlich nur durch die Gre des Hauptspeichers begrenzt. Mchten Sie innerhalb eines Strings die doppelten Hochkommata verwenden, so mssen Sie diese mittels Backslash \ maskieren. Das wrde dann so aussehen:

Die Konsolen-Ausgabe lautet: "C#" ist toll! Falls Sie sich die Escapesequenzen anschauen wollen, nehmen Sie sich das Kapitel ber die Literale erneut vor. Vielleicht ist Ihnen in den Sinn gekommen, dass Pfadangaben unter Windows einen oder mehrere Backslashs enthalten. Ein mglicher Pfad knnte folgendermaen lauten:

Sie erkennen sofort, dass da etwas nicht stimmt. Fahren Sie mit der Maus ber die roten Schlangenlinien unterhalb der Buchstaben nach den beiden Backslashs, so nennt Ihnen der Tooltip den Grund. Der Backslash leitet eine Escapesequenz ein und der nachfolgende Buchstabe l wird zur Sequenz gehrig angesehen. \l ist aber keine gltige Escapesequenz. Eigentlich wollen wir ja auch keine Escapesequenz, sondern einen String mit einer Pfadangabe. Die Lsung des Problems besteht in der Maskierung des Backslashs durch einen nachfolgenden Backslash.

184

Verbatim Strings
Es existiert jedoch noch eine andere Variante, um eine Zeichenkette so zu interpretieren, wie sie eigentlich vorgesehen ist. Also ohne Escapesequenzen. Dazu wird vor die Zeichenkette der Klammeraffe @ gesetzt.

Sie sehen, dass wir auf diese Weise den ursprnglichen String nutzen knnen. Sogar folgende Zeichenketten bereiten mit Verbatim-Strings kein Problem:

Die Konsolen-Ausgabe lautet: Das ist ja wirklich klasse! Sogar die eingerckte zweite Zeile wird korrekt auf der Konsole ausgegeben. Dotty: Wie kann ich aber innerhalb von Verbatim-Strings doppelte Hochkomma einsetzen? Den Backslash knnen wir aus den bekannten Grnden dafr nicht verwenden. Versuchen wir doch einfach mal zwei Hochkomma hintereinander zu setzen,

denn das ist genau die Lsung. Kommen wir jetzt zu einem wichtigen Thema: Dem Umgang mit Zeichenketten. Man mit Zeichenketten so einiges anfangen. Wir knnen sie in ihrer Gnze ausgeben oder nur einen Teil davon. Man kann in 185

ihnen nach einem bestimmten Teilwort suchen oder die Lnge einer Zeichenkette ermitteln. Das sind nur einige der sehr umfangreichen Mglichkeiten, die uns C# bietet. Fangen wir ganz einfach an.

Initialisierung
Initialisierung Version 1 Die Initialisierung eines Strings haben wir schon mehrfach kennen gelernt.

Die Deklaration + Initialisierung der Variablen strA vom Datentyp string findet in Zeile 16 statt. Es wird fr die Initialisierung der Zuweisungsoperator = verwendet. Die Zeichenkette, mit der die Variable strA initialisiert werden soll, steht dabei auf der rechten Seite in doppelten Anfhrungszeichen. Nach der Initialisierung ist die String-Variable unvernderlich (immutable). Was bedeutet das? Es bedeutet nicht, dass Sie die String-Variablen nach der Initialisierung nicht mit einem neuen Wert versehen knnen. Folgende Anweisungen sind natrlich auch mglich:

186

Durch die erneute Zuweisung wird jedoch das alte String-Objekt zerstrt und ein neues generiert, was, wie Sie sicherlich vermuten, mit einer schlechten Performance einhergeht. Es existiert eine StringBuilder-Klasse, die dieses Merkmal nicht aufweist. Doch dazu spter mehr. Initialisierung Version 2 Eine Zeichenkette ist - wie Sie schon wissen - eine (Ab)Folge einzelner Zeichen (Character). Wir knnen eine Array-Variable vom Datentyp char Deklarieren und Initialisieren.

Zeile Erklrung 16 18 Deklaration + Initialisierung des Arrays chrVar vom Datentyp char. Deklaration der Variablen strVar vom Datentyp string und nachfolgender Initialisierung durch ein String-Objekt. Nach der Eingabe der runden Klammer hinter new string meldet sich IntelliSense und wir erkennen, dass die Klasse achtfach berladen ist.

187

Die Parameterliste der zweiten Version zeigt uns, dass ein char-Array als bergabe mglich ist. Das ist genau das, was wir brauchen. 19 Ausgabe des Strings auf die Konsole.

Tab. 46:

Code-Erluterungen

Initialisierung Version 3 Wir knnen eine String ber ein neues String-Objekt generieren.

Zeile Erklrung 15 Deklaration + Initialisierung der Variablen intAnzahl fr die Anzahl der Character, die die Zeichenkette beinhalten soll. 16 Deklaration + Initialisierung der Variablen chrCharacter fr das Zeichen, das die Zeichenkette beinhalten soll. 17 Generierung der Zeichenkette mit den vorgegeben Parametern

Die Parameterliste der vierten Version zeigt uns, dass ein einzelnes Zeichen vom Datentyp char und eine Angabe ber die Anzahl der Zeichen als bergabe mglich ist. Das ist die Version, die wir hier bentigen.

Tab. 47: 188

Code-Erluterungen

Die Konsolen-Ausgabe lautet:

************

Leerstring zuweisen
Es existieren zwei Mglichkeiten, einem String eine leere Zeichenkette zuzuweisen.

Dotty: Hat eine String-Variable nach der Deklaration eigentlich implizit eine leere Zeichenkette inne? Ich glaube, ich hatte es schon erwhnt, dass eine String-Variable zu den Referenz-Typen und nicht zu den Werte-Typen gerechnet wird. Schauen wir uns den Zustand nach der Deklaration der Variablen im Debug-Modus an:

Nach dem Start mit F5 wechseln wir ber das Men Debuggen|Fenster|Lokal in das entsprechende Fenster und sehen,

189

dass der Variablen strA der Wert null zugewiesen wurde. Sie hat also keine leere Zeichenkette inne und Sie sollten auf jeden Fall vermeiden, eine nicht initialisierte Variable irgendwo in Ihrem Code zu verwenden. Setzen Sie sie wenn mglich auf null, so dass sie einen definierten Startwert besitzt.

Zeile 15 deklariert und initialisiert die String-Variable mit null. Die Klasse String bietet zur Abfrage auf null oder leerer Zeichenkette eine passende Methode IsNullOrEmpty() an.

Der Rckgabetyp ist bool und als Argument wird eine String-Variable erwartet.

Lnge einer Zeichenkette


Jeder initialisierte String hat eine bestimmte Anzahl von Zeichen. Die Summe aller Zeichen wird die Lnge der Zeichenkette genannt. Um diese zu bestimmen nutzen wir die Eigenschaft Length der Klasse String. Nach der Eingabe des String-Namen bietet uns IntelliSense eine Reihe von Klassen-Member zur Auswahl an. Dort finden Sie auch die einzige Eigenschaft Length.

190

Das entsprechende Programm schaut folgendermaen aus:

Und die Konsolen-Ausgabe lautet: Anzahl der Zeichen: 15 Bedenken Sie, dass auch ein Leerzeichen als eigenstndiges Zeichen gilt und mitgezhlt wird! 191

Einzelne Zeichen extrahieren


ber einen Index ist es mglich, ein einzelnes Zeichen einer Zeichenkette gezielt anzusprechen. Das vorliegende kleine Programm legt die Zeichenkette C# macht Spass in der Variablen strA ab.

Schauen wir uns zunchst an, wie eine Zeichenkette im Speicher abgelegt wird. Die folgende Tabelle weist in der ersten Zeile die Positionsnummern aus und die zweite Zeile deren Inhalt.

In Zeile 16 bzw. 17 wird das Zeichen des Strings strA ber einen Index angesprochen. Bedenken Sie jedoch, dass die Zhlweise bei Null beginnt. Der Positionswert 3 gibt demnach das 4. Zeichen (m) von links aus. Dotty: Ist das angesprochene Zeichen vom Datentyp string oder char? 192

Eine berechtigte Frage, die wir durch die folgende Anweisung mittels der Methode GetType() beantworten:

Die Konsolen-Ausgabe lautet: System.Char Noch Fragen? Auf diese Weise haben wir ein einziges Zeichen angesprochen. Was ist aber, wenn wir ab einer gewissen Position mehrere Zeichen erreichen wollen? Dafr existiert die Methode Substring(). Diese Methode ist zweifach berladen, wie uns IntelliSense mitteilt.

Die erste Version ermglicht es uns eine Anfangsposition fr die zu extrahierende Zeichenkette anzugeben. Es wird alles ab dieser Position bis zum Ende der Zeichenkette ausgegeben.

Zeile 15 liefert den Zeilstring ab Position 3 (ab dem 4. Zeichen) an die Konsole, die da lautet:

193

macht Spass Die zweite Variante von Substring() ermglicht es uns eine Lngenangabe mit zu bergeben. Sie funktioniert hnlich wie die erste Version, doch wir legen fest, wie viele Zeichen ab der Startposition bei der Ausgabe bercksichtigt werden.

Die Konsolen-Ausgabe lautet: macht

Zeichenketten verbinden bzw. verketten


Mchten Sie zwei oder mehre Zeichenkette miteinander verbinden, nutzen Sie einfach den +Operator.

194

Die Konsolen-Ausgabe lautet: C# macht riesigen Spass Concat Den gleichen Effekt erzielen Sie mit der Concat() der String-Klasse. eigens dafr kreierten Methode

Diese Methode akzeptiert dabei eine beliebige Anzahl von Argumenten. Es ist sogar mglich, ein Array vom Datentyp string zu bergeben.

Die Konsolen-Ausgabe lautet - wie sollte es anders sein:

195

C# macht Spass Es gibt noch eine weitere Mglichkeit, um Zeichenketten miteinander zu verbinden. Diese Variante erlaubt es ein Trennzeichen zwischen die einzelnen Zeichenketten zu platzieren, um sie ggf. spter mit der Methode Split() - auf die wir gleich noch kommen - wieder zu trennen. Join Die Methode lautet Join() und verbindet die String-Elemente eines Arrays miteinander.

Die Konsolen-Ausgabe lautet: C#;macht;Spass Beachten Sie, dass das Zeichen, was die einzelnen Strings zu einem zusammenfgt vom Datentyp string und nicht char ist. Das bedeutet wiederum, dass Sie mehr als nur ein Zeichen angeben knnen. Schauen Sie sich dazu auch die Methode Split() an. Sie ist das Gegenstck zu Join() und teilt Zeichenketten in ihre Bestandteile auf.

196

Innerhalb von Zeichenketten suchen


IndexOf() Es kommt nicht gerade selten vor, dass man eine oder mehrere Zeichenketten nach einem bestimmten Buchstaben oder Wort durchsuchen muss. Fr diese Aufgabe existiert z.B. die Methode IndexOf(). Sie liefert einen Wert bzw. Index zurck, ab der der gesuchte Buchstabe oder das Wort innerhalb einer Zeichenkette gefunden wurde.

Der Satz, der nach einem bestimmten Zeichen zu durchsuchen ist, wurde in Zeile 14 in der Variablen strSatz abgelegt. Das Suchzeichen ist in diesem Beispiel in der Variablen chrSuchzeichen in der Zeile 15 initialisiert worden. In Zeile 17 wird ber die Methode IndexOf() der Index ermittelt, ab der das Zeichen das erste Mal gefunden wurde. Die Konsolen-Ausgabe lautet: Position: 5 Die Methode IndexOf() ist mehrfach berladen und wir knnen ihr auer dem Suchzeichen noch eine Anfangsposition mit auf den Weg geben, ab der gesucht werden soll. Das ist ganz ntzlich, um z.B. alle Positionen vorkommender Suchzeichen zu ermitteln. Entfllt dieser Parameter wie in unserem Beispiel, beginnt die Suche links ab Index 0. Fhrt die Suche nicht zu einem Erfolg, wird der Wert -1 zurck geliefert. Das folgende Beispiel nutzt diese Mglichkeit, 197

eine Startposition festzulegen, um alle vorkommenden Suchzeichen zu ermitteln.

Die Konsolen-Ausgabe lautet: Position: Position: Position: Position: 5 9 27 28

Doch wie ist das mglich? Wrden wir nicht immer wieder eine neue Startposition festlegen, bekmen wir stets nur die erste auffindbare Position zurckgeliefert. Das wre in unserem Fall Position 5. Das Programm merkt sich jedoch in der Variablen intLast diese letzte gefundene Position (Zeile 21) und bergibt sie um den Wert 1 erhht beim nchsten Schleifendurchlauf (Zeile 20) der Methode IndexOf(). Dadurch wird gewhrleistet, dass nicht immer wieder eine schon erkannte Position bei der Suche bercksichtigt wird. Die while-Schleife fhrt die Anweisungen, die sich innerhalb der geschweiften Klammen hinter while befinden, so oft aus, bis der Ausdruck hinter while als logisch falsch (false) erkannt wird. In unserem Fall wrde das eine Endlosschleife bedeuten, da dort true - also logisch wahr - eingetragen wurde. 198

Gibt es trotzdem einen Ausweg aus diesem Dilemma? Klar! Zeile 22 bzw. 23 definieren eine Abbruchbedingung. Wurde der Wert -1 von IndexOf() zurckgeliefert, was bedeutet, dass die Suche erfolglos war, wird die breakAnweisung in Zeile 23 ausgefhrt, was wiederum bedeutet, dass die Schleife unmittelbar verlassen wird. Ist vielleicht etwas viel an dieser Stelle, doch Sie knnen getrost weiter lesen und spter hierher zurckkehren. Versuchen Sie doch einfach mal die Suche nicht mit einem einzelnen Zeichen (Character), sondern mit einer Zeichenfolge durchzufhren. LastIndexOf() Die Suche mittels IndexOf() startete stets von links und lieferte die Position des ersten Treffers. Wir knnen eine weitere Methode mit Namen LastIndexOf() nutzen, um die Position des letzten Treffers zu ermitteln. Es ist sozusagen der erste Treffer von rechts.

Die Konsolen-Ausgabe lautet: Position: 9

199

Umwandlung in Grobuchstaben
ToUpper() Die Klasse String stellt eine Methode zur Verfgung, mit der Sie alle Zeichen einer Zeichenkette in Grobuchstaben konvertieren knnen. Sie lautet ToUpper(). Dotty: Was soll das denn fr einen Grund haben? Sie haben ein Programm geschrieben, das auf die Eingabe des Benutzers wartet. Er soll eine Entscheidung treffen, ob er die Anwendung verlassen soll oder weiter macht. Von ihm wird also entweder ein ja oder ein nein erwartet. Nun kann man ein ja oder ein nein unterschiedlich schreiben: ja oder Ja bzw. nein oder Nein. Natrlich knnen Sie Ihr Programm so schreiben, dass es auf jede der mglichen Eingaben entsprechend reagiert. Doch es gibt eine elegantere Lsung. Wandeln Sie die vom Benutzer gemachte Eingabe einfach in Grobuchstaben. Anschlieend fhren Sie den Vergleich nur mit Grobuchstaben durch. Das funktioniert auf jeden Fall.

In den Zeilen 17 bzw. 19 wird ausschlielich auf Grobuchstaben verglichen. Es bedeutet, dass das Programm z.B. das ja bei folgenden Eingaben korrekt erkennt: ja, Ja, jA und JA. Das Beispiel fr ein nein erspare ich mir jetzt. 200

Umwandlung in Kleinbuchstaben
ToLower() Die Umwandlung in Kleinbuchstaben erreichen Sie mit der Methode ToLower().

Zeichenketten zerlegen
Split() Sie haben gesehen, wie Sie Zeichenketten aus einzelnen Teilen zusammen setzten knnen. Der umgekehrte Weg ist aber auch mglich. Es muss jedoch eine Information vorhanden sein, die dem Compiler mitteilt, wie bzw. an welcher Stelle er die vorliegende Zeichenkette zu zerlegen hat. Ein Satz besteht normalerweise aus einzelnen Wrtern, die durch mehrere Leerzeichen voneinander getrennt sind. Das ist genau die Frage, die wir uns stellen sollten: Wie sind die einzelnen Wrter voneinander getrennt? Es knnen die schon erwhnten Leerzeichen oder auch andere Zeichen wie z.B. das Semikolon, der Doppelpunkt sein. Die Methode, die diese Funktionalitt liefert nennt sich Split() und liefert das Ergebnis in einem Array zurck.

201

Zeile Erklrung 14 15 Deklaration + Initialisierung der Variablen strSatz Deklaration des Arrays strArray vom Datentyp string zur Aufnahme der einzelnen Wrter aus der Variablen strSatz. 16 Die Methode Split() zerlegt die Zeichenkette strSatz anhand des festgelegten Trennzeichens, das in unserem Fall ein Leerezeichen ist. 17 Die foreach-Schleife spricht jedes Element des Arrays strArray an und gibt den Inhalt in Zeile 18 auf der Konsole aus.

Tab. 48:

Code-Erluterungen

Die Konsolen-Ausgabe lautet: C# macht ne Menge Spass

Das Array strArray wrde folgendermaen initialisiert: Array-Index Array-Inhalt 0 C#

nach 1

Abarbeitung 2 ne

der 3

Methode

Split() 4

macht

Menge

Spass!

Dotty: Falls eine Zeichenkette unterschiedliche Trennzeichen enthlt, wie kann man trotzdem gewhrleisten, dass das Array korrekt initialisiert wird? Das folgende Programm enthlt ein Array, dessen einzelne Werte durch zwei unterschiedliche Trennzeichen (Delimiter) voneinander getrennt sind. Leerzeichen Semikolon 202

Die Konsolen-Ausgabe lautet in dem Fall: 10 23 17 19;23 9 21 Sie erkennen, dass die Werte 19 bzw. 23 nicht korrekt getrennt wurden, das Split nur das Leerzeichen als Delimiter akzeptiert hat. Wie knnen wir aber erreichen, dass die Sache so funktioniert, wie wir es vorhaben? Ganz einfach. Die Methode Split ist mehrfach berladen und akzeptiert als Trennzeichen ein Array vom Datentyp char. Dieses Array kann alle Trennzeichen enthalten, die Split bercksichtigen soll.

203

Zeile 16 beschreibt ein Array chrDelimiter vom Datentyp char mit den notwendigen Trennzeichen (Leerzeichen und Semikolon), um auf die Zeichenkette strSatz entsprechend zu reagieren. Nach dieser Modifikation arbeitet unser Programm wie gewnscht.

Zeichenketten vergleichen #1
Wenn wir z.B. Werte vom Datentyp int miteinander vergleichen, nutzten wir die schon erwhnten Vergleichsoperatoren == fr Gleichheit bzw. != fr Ungleichheit. Diese knnen wir auch fr Vergleiche von Zeichenketten heranziehen.

204

Zeichenketten vergleichen #2
Es existiert noch eine weitere Mglichkeit, Zeichenketten miteinander zu vergleichen. Wir bedienen uns dazu verschiedener Methoden. Das folgende Beispiel nutzt die Methode Equals der String-Klasse.

205

Sicherlich ist Ihnen beim Eintippen des Codes aufgefallen, dass mehrere berladene Versionen der Methode Equals existieren. IntelliSense liefert wieder unschtzbare Informationen:

Drei unterschiedliche Versionen werden angeboten, die unterschiedliche Argumente erwarten. Erste Version

Sie erwartet 2 Argumente des Datentyps object und liefert entweder true oder false zurck. Zweite Version

Sie erwartet 2 Argumente des Datentyps string und liefert entweder true oder false zurck. Dritte Version

Sie erwartet 3 Argumente und ist wirklich interessant. Sie gleicht der zweiten Version, jedoch mit der Mglichkeit, den Vergleich zu parametrisieren. Das dritte Argument ist vom Datentyp StringComparision und beinhaltet eine Enumeration (Aufzhlung). Tippen wir nach dem zweiten Argument das Komma gefolgt von einem Leerzeichen, bietet uns IntelliSense die Auswahlmglichkeit an: 206

Geben Sie nun einen Punkt . ein, um die einzelnen Aufzhlungselemente anzeigen zu lassen.

Die Sache wird interessant, wenn sie zwei Zeichenketten unabhngig der Kleinbzw. Groschreibung vergleichen wollen. Whlen Sie dazu folgende Syntax:

207

Obwohl die beiden Zeichenketten in den Zeilen 11 und 12 unterschiedlich initialisiert wurden (Spass ist einmal gro und einmal klein geschrieben), liefert das Ergebnis aufgrund der Einstellung CurrentCultureIgnoreCase Gleichheit zurck. Spielen Sie ein wenig mit den unterschiedlichen Mglichkeiten und lesen die Erklrungen von IntelliSense. Vierte Version Hier nun die vierte Version, die erweiterte Mglichkeiten des Vergleichs anbietet. Sie Nutzt die Methode Compare der String-Klasse. Und stellt eine Menge an Optionen aufgrund der 8-fach berladung zur Verfgung, die ich nicht alle Varianten ansprechen mchte, denn Sie sollten selbst ein wenig experimentieren.

208

Teilstring Einfgen
Insert() In eine bestehende Zeichenkette knnen Sie durch die Methode Insert() eine weitere Zeichenkette einfgen.

Die Konsolen-Ausgabe lautet: C# macht riesig Spass

Die notwendigen Daten sind: Einfgeposition Einfge-String

209

Unser kleines Programm fgt ab der Position 9 die neue Zeichenkette riesig ein. Bedenken Sie, dass der ursprngliche String strSatz unangetastet bleibt und nur die Methode Insert() den neu generierten String zurck liefert. Falls Sie den Originalstring manipulieren wollen, schreiben Sie folgende Anweisung:

Teilstring Ersetzen
Die Methode Replace() gestattet es uns, ein einzelnes Zeichen oder eine Zeichenkette innerhalb eines Strings zu ersetzten.

Die Konsolen-Ausgabe lautet: C# macht riesig Spass

Die Syntax ist: Ergebnis-String = <string>.Replace("alter String", "neuer String");

210

Teilstring Lschen
Um einen Teilstring aus einem bestehenden String zu lschen, nutzten Sie die Methode Replace. Auch sie ist berladen, so dass uns mehrere Mglichkeiten zur Verfgung stehen.

Die erste berladung bietet uns einen Startindex als Parameter an, der festlegt, ab welcher Position der Zeichenkette bis zum rechten Ende gelscht werden soll.

Die Konsolen-Ausgabe lautet: 012 Sie fragen sich vielleicht warum. Ganz einfach, denn der Startindex beginnt bei 0 und demnach liegt die Ziffer 3 auf der dritten Position ab der die Zeichenkette nach rechts hin gesehen gelscht wird. brig bleiben die Positionen 0 bis 2. Die zweite berladung gibt uns die Wahl, wie viele Zeichen ab dem Startindex gelscht werden sollen.

211

Der folgende Code wurde um die Anzahl der zu lschenden Zeichen erweitert:

Die Konsolen-Ausgabe lautet: 012789 Die Lsung ist wiederum ganz simpel. Ab der dritten Position werden 4 Zeichen gelscht (3, 4, 5, 6) und brig bleiben auf der linken Seite 0, 1, 2 und auf der rechten Seite 7, 8, 9.

ASCII-Code ermitteln
Vielleicht stellen Sie sich die Frage, wie Sie anhand eines einzelnen Zeichens (Datentyp char) den entsprechenden ASCII-Code ermitteln.

212

Dotty: Wieso kann ich eine Variable vom Datentyp char einer Variablen des Datentyps int zuweisen? Schauen wir uns noch einmal die implizite Datenkonvertierung an. Sie erlaubt die Konvertierung eines Datentyps in einen anderen, wenn kein Informationsverlust zu befrchten ist. Also z.B. eine Short-Variable (16-Bit Datenbreite) in eine Integer-Variable (32-Bit Datenbreite). Ebenso ist es mglich, den Datentyp char in einen vom Typ int zu konvertieren.

ASCII-Zeichen ermitteln
Der umgekehrte Weg ist auch mglich. Sie haben einen ASCII-Code und mchten das entsprechende ASCII-Zeichen bestimmen. Die Umwandlung einer Variablen des Datentyps int kann nicht implizit in den eines vom Datentyp char erfolgen.

Sie erinnern sich: Mglicher Datenverlust! Deshalb mssen wir den CastOperator des Zieldatentyps angeben:

213

Sie knnen natrlich auch die Konvertierungsmethode Convert.ToChar() anwenden.

214