49

C# von Kopf bis Fuß

Embed Size (px)

DESCRIPTION

Dieses Standardwerk aus der beliebten „von Kopf bis Fuß“-Reihe nimmt den Leser auf eine Erlebnistour durch C#, bei der sowohl seine beiden Gehirnhälften als auch seine Lachmuskeln stimuliert werden. C# von Kopf bis Fuß ist ein unterhaltsames und visuell ansprechendes Arbeitsbuch für die objektorientierte Programmierung mit C# und der Visual Studio IDE. In der 3. Auflage deckt es C# 5 und das .NET Framework 4.5 ab. Behandelt werden alle zentralen Themen, von den Sprachgrundlagen bis zur Garbage Collection, den Erweiterungsmethoden und Animationen mit Double-Buffering. Der Leser lernt außerdem, wie er die Syntax von C# und die Abfrage von Datenquellen mit LINQ meistert. Neu aufgenommen wurden zwei Kapitel zur App-Programmierung; u.a. wird exemplarisch eine Windows Phone App entwickelt. Wer dieses Buch durchgearbeitet hat, ist ein kompetenter C#-Programmierer, der umfangreiche Anwendungen entwerfen und programmieren kann. http://www.oreilly.de/catalog/hfcsharp3ger/

Citation preview

Page 1: C# von Kopf bis Fuß
Page 2: C# von Kopf bis Fuß

Der Inhalt

vii

Der Inhalt (im Überblick)

Der Inhalt (jetzt ausführlich)

Ihr Gehirn und C#. Sie versuchen, etwas zu lernen, und Ihr Hirn tut sein Bestes, damit das

Gelernte nicht hängen bleibt. Es denkt nämlich: »Wir sollten lieber ordentlich Platz für wichtigere

Dinge lassen, z. B. für das Wissen darüber, welche Tiere einem gefährlich werden könnten oder dass

es eine ganz schlechte Idee ist, nackt Snowboard zu fahren.« Tja, wie schaffen wir es nun, Ihr Gehirn

davon zu überzeugen, dass Ihr Leben davon abhängt, etwas über C# zu wissen?

Einführung

Für wen ist dieses Buch? xxxWir wissen, was Ihr Gehirn denkt xxxiMetakognition xxxiiiMachen Sie sich Ihr Hirn untertan xxxvWas Sie für dieses Buch brauchen xxxviLies mich xxxviiDie Fachgutachter xxxviiiDanksagungen xxxix

Einführung xxix1 Erste Schritte mit C#: Apps im Handumdrehn 12 Es ist alles bloß Code: Unter der Motorhaube 533 Objekte: Orientieren Sie sich!: Aufgabenspezifischer Code 1014 Typen und Referenzen: Es ist 10:00. Wissen Sie, wo Ihre Daten sind? 141 C# Workshop 1: Ein Tag beim Rennen 187 5 Kapselung: Alles zu zeigen, ist nicht immer richtig 1976 Vererbung: Der Stammbaum Ihres Objekts 2377 Schnittstellen und abstrakte Klassen: Klassen, die Versprechen halten 2938 Enums und Auflistungen: Daten in Massen speichern 3519 Dateien lesen und schreiben: Speichere das Array, rette die Welt 409 C# Workshop 2: Die Suche 465 10 Windows Store-Apps mit XAML: Ihre Apps auf die nächste Stufe bringen 48711 XAML, Datei-I/O und Datenkontraktserialisierung: Entschuldigen Sie die Unterbrechung 53512 Exception-Handling: Fehler-Prävention 56913 Captain Amazing: Der Tod des Objekts 61114 Datenabfrage und App-Bau mit LINQ: Bekommen Sie Ihre Daten in den Griff 64915 Events und Delegates: Was Ihr Code macht, wenn Sie nicht gucken 70116 App-Entwurf mit dem MVVM-Muster: Tolle Apps, außen wie innen 745 C# Workshop 3: Invaders 80717 Bonusprojekt: Erstellen Sie eine Windows Phone-App 831A Was übrig bleibt: Die Top 11 der Themen, die es nicht ins Buch geschafft haben 845 Index 877

Page 3: C# von Kopf bis Fuß

Hier fängt ein neues Kapitel an 141

Diese Daten holt gleich die Müllabfuhr!

Typen und Referenzen4

Es ist 10:00. Wissen Sie, wo Ihre Daten sind?

Datentyp, Datenbank, Lieutenant Commander Data … all das ist wichtiges Zeug. Ohne Daten sind Ihre Programme nutzlos. Sie brauchen

Informationen von Ihren Anwendern und nutzen diese, um andere Informationen nachzuschlagen

oder neue Informationen zu produzieren, die Sie dann wieder an Ihre User zurückliefern. Eigentlich

schließt fast alles, was Sie beim Programmieren tun, auf die eine oder andere Weise Arbeit mit Da-

ten ein. In diesem Kapitel werden Sie alles über die Datentypen von C# lernen, erfahren, wie Sie in

Ihrem Programm mit Daten arbeiten, und Sie werden sogar auf einige schmutzige Geheimnisse bei

Objekten stoßen (psst … auch Objekte sind Daten).

Page 4: C# von Kopf bis Fuß

142 Kapitel 4

Der Typ e iner Variablen best immt, welche Art Daten sie speichern kannIn C# sind jede Menge Typen eingebaut, die jeweils Daten unterschiedlicher Art speichern. Einige der am weitesten verbreiteten haben Sie bereits gesehen, und Sie wissen, wie man sie verwendet. Aber es gibt noch ein paar, die Ihnen bisher nicht begegnet sind, die aber ebenfalls ziemlich praktisch sein können.

Typen, die Sie permanent verwendenEs sollte Sie nicht überraschen, dass int, string, bool und float die gebräuchlichsten Typen sind.

≥ int kann ganze Zahlen von –2.147.483.648 bis 2.147.483.647 speichern.

≥ string kann Text beliebiger Länge (einschließlich des leeren Strings "") speichern.

≥ bool ist ein Boolescher Wert – er ist entweder true oder false.

≥ double kann eine reelle Zahl von ±5,0 × 10−324 bis ±1,7 × 10308 mit bis zu 16 signifikanten Stellen speichern. Dieser Bereich scheint seltsam und kompliziert, ist eigentlich aber ziemlich einfach. »Signifikante Stellen« bezieht sich auf die Genauigkeit der Zahl: 35.048.410.000.000, 1.743.059, 14,43857 und 0,00004374155 haben jeweils 7 signifikante Stellen. 10308 bedeutet, dass Sie Zahlen bis zu einer Größe von 10308 (oder 1 gefolgt von 308 Nullen) speichern können – solange sie 16 oder weniger signifikante Stellen haben. Auf der anderen Seite des Wertebereichs bedeutet 10–324, dass Sie eine Zahl so klein wie 10–324 (oder einen Dezimaltrenner gefolgt von 324 Nullen gefolgt von 1) speichern können … aber nur, wie Sie sicher schon erraten haben, solange sie 16 oder weniger signifikante Stellen hat.

≥ byte speichert ganze Zahlen zwischen 0 und 255.

≥ sbyte speichert ganze Zahlen von –128 bis 127.

≥ short speichert ganze Zahlen von –32.768 bis 32.767.

≥ ushort speichert ganze Zahlen von 0 bis 65.535.

≥ uint speichert ganze Zahlen von 0 bis 4.294.967.295.

≥ long kann jede ganze Zahl zwischen minus und plus 9 Milliarden Milliarden speichern.

≥ ulong kann jede ganze Zahl zwischen 0 und 18 Milliarden Milliarden speichern.

Diese Typen nutzen Sie oft, wenn Sie ein Problem lösen, bei dem das »Überschlagen«, das Ihnen in ein paar Minuten begegnen wird, wirklich nützlich ist.

Das »u« steht für »unsigned« (vorzeichenlos).

Nicht mein Typ

Eine ganze Zahl hat keinen Nachkommateil.

Weitere Typen für ganze ZahlenEs gab einmal eine Zeit, in der Computerspeicher verdammt teuer und Computerprozes-soren verdammt langsam waren. Und, glauben Sie es oder nicht, wenn man den falschen Datentyp verwendete, konnte das ein Programm ernstlich ausbremsen. Glücklicherweise haben die Zeiten sich geändert, und wenn man eine ganze Zahl speichern muss, kann man meist sorglos einen int verwenden. Manchmal aber brauchen Sie etwas Größeres … und gelegentlich auch mal etwas Kleineres. Deswegen gibt Ihnen C# weitere Optionen:

Das »s« in sbyte steht für »signed« (mit Vorzeichen), d. h., der Wert kann negativ sein (das Vorzeichen ist das Minuszeichen).

Alle Projekte in diesem Kapitel sind Windows Forms-Anwen-

dungen. Wenn wir Ihnen in diesem Kapitel sagen, dass Sie ein neues Projekt erstel-

len sollen, den Projekttyp aber nicht angeben, können Sie davon ausgehen, dass es sich um eine Windows Forms-Anwendung handelt, die mit Visual Studio für Windows

Desktop erstellt wird.

Derartige Zahlen bezeichnet man als »Gleitkommazah-len« ... im Gegen-satz zu »Fest-kommazahlen«, die immer die gleiche Anzahl von Nach-kommastellen haben.

Page 5: C# von Kopf bis Fuß

Sie sind hier 4 143

Typen und Referenzen

Als Sie die Value-Eigenschaft des NumericUpDown-Steuer-elements verwendeten, haben Sie einen decimal eingesetzt.

Mit dem Windows-Rechner können Sie zwischen dezimalen Zahlen (zur Basis 10) und binären Zahlen (Zah-len zur Basis 2, die nur mit 0 und 1 geschrieben werden) wechseln – gehen Sie in den Wissenschaftlich-Modus, geben Sie eine Zahl ein und klicken Sie auf Bin, um die Zahl nach binär zu konvertieren. Klicken Sie dann auf Dec, um die Konvertierung rückgängig zu machen. Geben Sie jetzt einige der Ober- und Unter-grenzen für die ganzzahligen Typen (wie –32.768 und 255) ein und wandeln Sie sie in binär um. Haben Sie eine Idee, warum C# gerade diese Grenzen wählt?

Typen zum Speichern echt riesiger und echt winziger ZahlenManchmal sind 16 signifikante Stellen (und ein Wertebereich für astronomisch große oder subatomar kleine Zahlen) etwas übertrieben, und manchmal sind es einfach nicht genug. Deswegen stellt Ihnen C# weitere Typen zur Verfügung:

≥ float speichert Zahlen von ±1,5 × 10–45 bis ±3,4 × 1038 mit 7 signifikanten Stellen.

≥ double speichert Zahlen von ±5.0 × 10–324 bis ±1.7 × 10308 mit 15 bis16 signifikanten Stellen.

≥ decimal speichert Zahlen von ±1,0 × 10–28 bis ±7,9 × 1028 mit 28 bis 29 signifikanten Stellen.

Auch Literale haben TypenGeben Sie in ein C#-Programm direkt eine Zahl ein, nutzen Sie ein Literal … und jedem Literal wird automatisch ein Typ zugewiesen. Das können Sie selbst sehen – geben Sie einfach folgende Zeile ein, um einer int-Variablen den Wert 14.7 zuzuweisen:

int meinInt = 14.7;

Versuchen Sie, das Programm zu erstellen, erscheint dies:

Den gleichen Fehler erhalten Sie, wenn Sie versuchen, eine int-Variable auf eine double-Variable zu setzen. Die IDE sagt Ihnen damit, dass das Literal 14.7 einen Typ hat – und der ist double. Sie können den Typ in float ändern, indem Sie ans Ende ein F anhängen (14.7F). Und 14.7M ist ein decimal.

Versuchen Sie, einer float- oder einer decimal-Variablen einfach so ein Literal zu-zuweisen, liefert Ihnen die IDE eine hilfreiche Meldung, die Sie daran erinnert, das richtige Suffix hinzuzufügen. Cool!

Ein paar weitere nützliche eingebaute TypenGelegentlich muss man einzelne Zeichen wie Q, 7 oder $ speichern. Dazu nutzt man den Typ char. Literale Werte für char stehen immer in einfachen Anführungszei-chen ('x', '3'). In die Anführungszeichen können Sie auch Escape-Sequenzen einschließen ('\n' ist ein Zeilenumbruch, '\t' ein Tabulator). Sie schreiben Escape-Sequenzen in Ihrem C#-Code mit mehreren Zeichen, aber Ihr Programm speichert jede Escape-Sequenz im Speicher als ein einziges Zeichen.

Abschließend gibt es noch einen wichtigen Typ: object. Sie haben bereits gesehen, dass man Objekte erstellt, indem man Instanzen von Klassen erstellt. Jedes so erstellte Objekt kann einer Variablen vom Typ object zugewiesen werden. Im Verlauf dieses Kapitels werden Sie alles über Objekte und Variablen lernen, die auf Objekte verweisen.

In Kapitel 9 lernen Sie mehr zum Verhältnis von char und byte. 

double wird erheblich häufiger genutzt als float. Viele XAML-Eigenschaften nut-zen double-Werte.

Ein »Literal« ist einfach eine Zahl, die Sie in Ihren Code eingeben. Die 5 im Ausdruck »int i = 5;« ist ein Literal.

»M« steht für »money« (Geld) — kein Scherz!

Der Windows-Rechner bietet den nützlichen »Programmierermodus«, in dem Sie binäre und dezimale Werte nebeneinander sehen können!

Bei Wäh-rungsbeträgen sollten Sie in der Regel decimal-Werte nutzen.

Kopf- nuss

Page 6: C# von Kopf bis Fuß

144 Kapitel 4

Eine Variable ist wie e in Daten-to-go-BecherAlle Datentypen brauchen Speicherplatz. (Erinnern Sie sich an den Heap aus dem letzten Kapitel?) Ihre Aufgabe beinhaltet also, dass Sie sich darüber Gedanken machen, wie viel Speicherplatz Sie benötigen, wenn Sie in einem Programm einen String oder eine Zahl verwenden. Das ist einer der Gründe dafür, dass Sie Variablen verwenden. Über sie reser-vieren Sie so viel Platz im Speicher, wie Sie für Ihre Daten benötigen.

Stellen Sie sich eine Variable wie einen Becher vor, in dem Sie Ihre Daten lagern. C# nutzt eine Menge unterschiedlicher Arten von Bechern, um unterschiedliche Arten von Daten festzuhalten. Und genau wie die verschiedenen Bechergrößen im Café haben auch Variablen unterschiedliche Größen.

long int short byte 64 32 16 8

float double decimal 32 64 128

Zahlen mit Nachkommastellen werden anders gespeichert als ganze Zahlen. Die meisten Ihrer Zahlen mit Nachkommastellen können Sie mit float spei-chern, dem kleinsten Datentyp zur Speicherung von Dezimalzahlen. Wenn Sie eine höhere Genauigkeit brauchen, verwenden Sie double, und für das Schreiben von Finanzanwendungen, bei denen die Zahlen sehr genau sein müssen, sollten Sie den Typ decimal nutzen. Es geht aber nicht immer um Zahlen. (Sie würden ja auch nicht erwarten, einen heißen Kaffee in einem Plastikbecher zu bekommen und einen kalten in einem Papierbecher.) Der C#-Compiler kann auch mit Zeichen und anderen nicht numerischen Typen umgehen. Der Typ char speichert ein Zeichen, und string wird für viele aneinandergehängte Zeichen verwendet. Für eine string-Variable muss keine Größe vorgegeben werden. Sie wird so erwei-tert, dass sie so viele Daten aufnehmen kann, wie Sie darin speichern müssen. Der Typ bool wird zur Speicherung von Wahr/Falsch-Werten verwendet, wie Sie sie in Ihren if-Anweisungen verwendet haben.

bool char string 8 16

long nutzen Sie für Zahlen, die echt groß werden können.

Für ganze Zahlen wird normalerweise int verwendet.

Es speichert Zahlen bis 2.147.483.647.

Das ist die Anzahl der Bits, die für Ihre Variable im Speicher reserviert werden, wenn Sie sie deklarieren.

Das sind Typen für Zahlen

mit Nachkommaanteil. Größere

Typen speichern Werte mit mehr

Nachkommastellen.

Ein short speichert Zahlen bis 32.767.

byte speichert Zahlen zwischen 0 und 255.

von der Größe des Strings abhängig

Einen Eisbecher float zum Mitnehmen, bitte

Es landen nicht alle Daten auf dem Heap. Werttypen werden üblicherweise in einem anderen Speicherbereich festgehalten, der als Stack bezeichnet wird. In Kapitel 14 werden Sie mehr über ihn erfahren.

Page 7: C# von Kopf bis Fuß

Sie sind hier 4 145

Typen und Referenzen

Drei dieser Anweisungen lassen sich nicht kompilieren, weil sie versuchen, zu große Daten oder Daten des falschen Typs in eine Variable zu stecken. Kreisen Sie sie ein.

10 Kilo Daten in e inem 5-Kilo-SackDeklarieren Sie eine Variable eines bestimmten Typs, wird sie vom Compiler auch so betrachtet. Selbst wenn ein Wert nicht einmal in die Nähe der Obergrenze des Typs kommt, den Sie deklariert haben, blickt der Compiler nur auf den Becher, in dem er sich be-findet, nicht auf den Wert darin. Folgendes funktioniert also nicht:

int meilenUnterDemMeer = 20000;

short wenigerMeilen = meilenUnterDemMeer;

20.000 würde in einen short passen. Kein Problem. Aber da meilenUnterDemMeer als int deklariert ist, berücksichtigt der Compiler nur die maximale Größe eines int und sieht, dass diese zu groß für einen short-Container ist. Diese Übersetzung führt der Compiler nicht im Vorübergehen für Sie aus. Sie müssen sicherstellen, dass Sie den richtigen Typ für die Daten verwenden, mit denen Sie arbeiten.

int stunden = 24;

short y = 78000;

bool fertig = ja;

short RPM = 33;

int kontostand = 345667 - 567;

string drohung = "Deine Mutter";

byte tage = 365;

long radius = 3;

char initial = 'S';

string monate = "12";

20.000

int

short

Der Compiler sieht nur, dass ein int in einen short gesteckt werden soll (was nicht funktioniert). Um den Wert im Becher kümmert er sich nicht.

Das ist vernünftig. Was wäre, wenn Sie später einen größeren Wert in den int-Becher steckten, der nicht mehr in den short-Becher passt? Der Compiler versucht nur, Sie zu schützen.

Spitzen Sie Ihren Bleistift

Page 8: C# von Kopf bis Fuß

146 Kapitel 4

Drei dieser Anweisungen lassen sich nicht kompilieren, weil sie versuchen, zu große Daten oder Daten des falschen Typs in eine Variable zu stecken. Sie sollten sie einkreisen.

short y = 78000;

bool fertig = ja;

byte tage = 365;In einen byte pas-sen nur Werte bis 256. Für diese Zahl bräuchten Sie einen short.

Der Typ short speichert Zahlen von -32.767 bis 32.768. Die Zahl ist zu groß!

Einem bool können Sie nur die Werte »true« oder »false« zuweisen.

Auch eine Zahl der richt igen Größe können Sie nicht e infach irgendeiner Variablen zuweisen

Schauen wir, was passiert, wenn Sie versuchen, einer int-Variablen einen decimal-Wert zuzuweisen.

Erzeugen Sie ein neues Projekt und fügen Sie einen Button ein. Fügen Sie dann der Click()-Methode des Buttons folgende Zeilen hinzu:

decimal meinDezimalWert = 10; int meinIntWert = meinDezimalWert;

MessageBox.Show("meinIntWert ist " + meinIntWert);

1

Sehen Sie sich an, wie die IDE herausfand, dass Ihnen wahrschein-lich eine Umwand-lung fehlt.

Versuchen Sie, das Programm zu erstellen. Oh, Sie erhalten diesen Fehler:2

Lassen Sie den Fehler verschwinden, indem Sie einen Cast (auch Umwandlung genannt) einfügen, der den decimal zu einem int macht. Nachdem Sie die zweite Zeile in der nachfolgenden Weise geändert haben, lässt sich das Programm kompilieren und ausführen:

int meinIntWert = (int) meinDezimalWert;

3

Umwandlungsaufruf

Dies ist die Umwandlung von einem decimal-Wert in einen int.

Tun Sie das!

Mehr zu Werttypen in C# erfahren Sie hier:http://msdn.microsoft.com/de-de/library/s1ax56ch.aspx

LösungSpitzen Sie Ihren Bleistift

Was ist passiert?Der Compiler lässt Sie einer Variablen keine Werte des falschen Typs zuweisen – selbst wenn die Vari-able den Wert aufnehmen könnte –, weil das der Grund für eine Unzahl von Fehlern ist. Der Compi-ler hilft Ihnen, indem er Ihnen den richtigen Weg weist. Wenn Sie umwandeln, ist das, als würden Sie dem Compiler ein Versprechen geben: »Ich weiß, die Typen sind unterschiedlich, aber in diesem Fall wird trotzdem nichts schiefgehen!« C# versucht dann, die Daten in die neue Variable zu packen.

Page 9: C# von Kopf bis Fuß

Sie sind hier 4 147

Typen und Referenzen

Wandeln Sie e inen zu großen Wert um, passt C# ihn automatisch an

Gerade haben Sie gesehen, dass ein decimal in einen int umgewandelt werden kann. Es ist sogar so, dass jede Zahl in jede andere Zahl umgewandelt werden kann. Aber das heißt nicht, dass der Wert bei der Umwandlung intakt bleibt. Wandeln Sie eine int-Variable, die auf 365 gesetzt ist, in einen byte um, ist 365 zu groß für den byte. Aber anstelle einer Fehlermeldung wird der Wert einfach überschlagen: Beispielsweise hat 256, in einen byte umgewandelt, den Wert 0, 257 den Wert 1, 258 den Wert 2 usw. bis 365, das zu 109 wird. Und sind Sie wieder bei 255 angekommen, »überschlägt« der umgewandelte Wert wieder zu null.

In meinen Dialogfenstern habe ich ständig Zahlen und

Strings kombiniert, seit ich in Kapitel 2 Schleifen kennen-gelernt habe! Habe ich mich da die ganze Zeit schon auf Umwandlungen verlassen?

Ja! Der +-Operator wandelt um.Dort haben Sie den +-Operator verwendet, der automatisch eine Reihe von Um-wandlungen für Sie durchführt – aber er ist darin ziemlich gewandt. Wenn Sie mit + eine Zahl oder einen Booleschen Wert zu einem String hinzuaddieren, wandelt er den Wert automatisch in einen String um. Verwen-den Sie + (oder *, / oder -) mit unterschiedli-chen Typen, wird der kleinere Typ auto-matisch in den größeren umgewandelt. Hier ist ein Beispiel:

int meinInt = 36;

float meinFloat = 16.4F;

meinFloat = meinInt + meinFloat;

Da ein int in einen float passt, ein float aber nicht in einen int, wandelt der +-Ope-rator meinInt in einen float um, bevor meinFloat hinzuaddiert wird.

Weisen Sie einem float einen Zahl-wert zu , müssen Sie ans Ende der Zahl F anhängen, um dem Compiler zu sagen, dass es ein float und kein double ist.

Typen können nicht immer in andere Typen umgewandelt werden. Erzeugen Sie ein neues Projekt mit einem Button und stecken Sie in seine Methode diese Anweisungen. Beim Erstellen des Programms erhalten Sie Massen an Fehlern. Streichen Sie die Anweisungen durch, die zu Fehlern führen. So sehen Sie, welche Typen umgewandelt werden können und welche nicht!

int einInt = 10;

byte einByte = (byte)einInt;

double einDouble = (double)einByte;

bool einBool = (bool)einDouble;

string einString = "false";

einBool = (bool)einString;

einString = (string)einInt;

einString = einInt.ToString();

einBool = (bool)einByte;

einByte = (byte)einBool;

short einShort = (short)einInt;

char einChar = 'x';

einString = (string)einChar;

long einLong = (long)einInt;

decimal einDecimal = (decimal)einLong;

einString = einString + einInt + einByte + einDouble + einChar;

Typen und Referenzen

Wandeln Sie selbst um!Wie die Umwandlung Zahlen »überschlägt«, ist kein Geheimnis – das können Sie selbst. Starten Sie den Windows-Rechner, wech-seln Sie in den Wissenschaftlich-Modus und berechnen Sie 365 Mod 256 (über den »Mod«-Button, der eine Modulo-Berech-nung durchführt ). Sie erhalten 109.

Spitzen Sie Ihren Bleistift

Page 10: C# von Kopf bis Fuß

148 Kapitel 4

Eine echte Konvertierung

long l = 139401930;

short s = 516;

double d = l - s;

d = d / 123.456;

MessageBox.Show("Die Antwort ist " + d);

Der +-Operator kann den double in einen string umwandeln.

Der --Operator zieht den short von dem long ab, und der =-Operator wandelt das Ergebnis in einen double um.

Einige Umwandlungen macht C# automatischEs gibt zwei wichtige Konvertierungen, die keine explizite Umwandlung verlangen. Die erste wird immer automatisch durchgeführt, wenn Sie arithmetische Operatoren verwenden, wie in diesem Beispiel:

Außerdem konvertiert C# Typen automatisch für Sie, wenn Sie den +-Operator einsetzen, um Strings zu verketten (was ein-fach nur heißt, einen String an das Ende eines anderen anzuhän-gen, wie Sie es bei den Dialogfenstern gemacht haben). Wenn Sie mit dem +-Operator einen String mit etwas verketten, das einen anderen Typ hat, wandelt er Zahlen automatisch in Strings um. Sehen Sie hier ein Beispiel dazu – die ersten beiden Zeilen sind in Ordnung, aber die dritte lässt sich nicht kompilieren:

long x = 139401930;

MessageBox.Show("Die Antwort ist " + x);

MessageBox.Show(x);

Der C#-Compiler spuckt einen Fehler aus, der irgendetwas von einem ungültigen Argument sagt (als Argument werden die Dinge bezeichnet, die Sie dem Parameter einer Methode übergeben). Das liegt daran, dass der Parameter für Messa-geBox.Show() ein string ist und dieser Code einen long, also den falschen Typ für diese Methode, übergibt. Aber Sie können ihn sehr leicht in einen String umwandeln, indem Sie seine ToString()-Methode aufrufen. Diese Methode ist ein Member jedes Werttyps und Objekts. (Alle Klassen, die Sie selbst erstellen, haben eine ToString()-Methode, die den Na-men der Klasse liefert.) Auf diese Weise können Sie x in etwas konvertieren, das MessageBox.Show() verwenden kann:

MessageBox.Show(x.ToString());

Typen können nicht immer in andere Typen umgewandelt werden. Erzeugen Sie ein neues Projekt mit einem Button und stecken Sie in seine Methode diese Anweisungen. Beim Erstellen des Programms erhalten Sie Massen an Fehlern. Haben Sie die Anweisungen durchgestrichen, die zu Fehlern führen? So sehen Sie, welche Typen umgewandelt werden können und welche nicht!

int einInt = 10;

byte einByte = (byte)einInt;

double einDouble = (double)einByte;

bool einBool = (bool)einDouble;

string einString = "false";

einBool = (bool)einString;

einString = (string)einInt;

einString = einInt.ToString();

einBool = (bool)einByte;

einByte = (byte)einBool;

short einShort = (short)einInt;

char einChar = 'x';

einString = (string)einChar;

long einLong = (long)einInt;

decimal einDecimal = (decimal)einLong;

einString = einString + einInt + einByte + einDouble + einChar;

LösungSpitzen Sie Ihren Bleistift

Page 11: C# von Kopf bis Fuß

Sie sind hier 4 149

Typen und Referenzen

Beim Aufruf e iner Methode müssen die Variablen den Typen der Parameter entsprechen

Versuchen Sie, MessageBox.Show(123) aufzurufen, übergeben Sie Message-Box.Show() ein Literal (123) statt eines Strings. Die IDE lässt Sie Ihr Programm nicht erstellen. Stattdessen meldet sie Ihnen einen Fehler: »1-Argument: kann nicht von ›int‹ in ›string‹ konvertiert werden.« Manchmal kann C# die Umwandlung automatisch durchführen – beispielsweise wenn eine Methode einen int erwartet, Sie ihr aber einen short übergeben –, aber bei int- und string-Werten kann es das nicht.

Aber MessageBox.Show() ist nicht die einzige Methode, die Ihnen Compiler-Fehler meldet, wenn Sie versuchen, ihr eine Variable zu übergeben, deren Typ dem Parameter nicht entspricht. Das tun alle Methoden, sogar die, die Sie selbst schreiben. Sie können das testen, indem Sie diese absolut zulässige Methode in eine Klasse eingeben:

Liefert Ihnen der Compiler einen » Ungültige Argu­mente«­Fehler, haben Sie ver­sucht, eine Metho­de mit Werten aufzurufen, deren Typen den Metho­denparametern nicht entsprechen.

public int MeineMethode(bool jaNein) {

if (jaNein) { return 45; } else { return 61; }

}

Sie funktioniert wunderbar, solange Sie ihr übergeben, was sie erwartet (einen bool) – rufen Sie MeineMethode(true) oder MeineMethode(false) auf, kompiliert das Programm problemlos.

Aber was ist, wenn Sie stattdessen einen int oder einen string übergeben? Die IDE liefert Ihnen einen ähnlichen Fehler wie den, den Sie erhalten haben, als Sie Message-Box.Show() 123 übergeben hatten. Übergeben Sie jetzt einen Booleschen Wert, aber versuchen Sie, den Rückgabewert einem string zuzuweisen oder an MessageBox.Show() zu übergeben. Auch das geht nicht – die Methode liefert einen int, nicht den string, den MessageBox.Show() erwartet.

Zur Erinnerung – der Code, der diese Methode auf-

ruft, muss den Parameter nicht als Variable namens

jaNein angeben. Er muss einfach nur einen Booleschen

Wert oder eine beliebige Boolesche Variable angeben.

jaNein heißt er lediglich innerhalb der Methode.

Sie sind hier 4 149

, den MessageBox.Show() erwartet.

if-Anweisungen prüfen immer auf WahrheitIst Ihnen aufgefallen, dass wir die if-Anweisung folgendermaßen geschrieben haben:

if (jaNein) {Wir mussten nicht explizit »if (jaNein == true)« sagen. Grund dafür ist, dass eine if- Anweisung

immer prüft, ob etwas wahr ist. Wollen Sie prüfen, ob etwas falsch ist, nutzen Sie das ! (ein Ausrufe-

zeichen bzw. den NICHT-Operator). »if (!jaNein)« ist das Gleiche wie »if (jaNein == false)«.

In unseren Codebeispielen werden wir von jetzt an in der Regel »if (jaNein)« oder »if (!jaNein)«

verwenden und nicht explizit prüfen, ob ein Boolescher Wert true oder false ist.

Einer Variablen, einem Parameter oder einem Feld mit dem Typ object können Sie alles zuweisen.

Parameter sind das, was Sie in Methoden definieren, Argumente sind das, was Sie ihnen übergeben. Eine Methode kann einen int-Parameter haben und dennoch ein byte-Argument akzeptieren.

Das haben Sie im »Die Menschheit retten«-Code gemacht. Nehmen Sie sich diesen noch einmal vor und schauen Sie, ob Sie die ent-sprechende Stelle finden.

Page 12: C# von Kopf bis Fuß

150 Kapitel 4

In C# gibt es ungefähr 77 reservierte Wörter. Diese Wörter werden durch den C#-Compiler reserviert und dürfen nicht als Variablennamen verwendet werden. Wenn Sie mit dem Buch durch sind, werden Sie sie alle gut kennen. Hier sind einige, die Sie schon benutzt haben. Schreiben Sie, was Ihrer Meinung nach diese Wörter in C# tun.

Diese Plätze sind reserviert

namespace

for

class

public

else

new

using

if

while

Antworten auf Seite 182.

Eigentlich gibt Ihnen C# eine Möglichkeit, reservierte Schlüsselwörter als Variablennamen zu nutzen, indem Sie ihnen ein @ voranstellen. Das können Sie auch mit nicht reservierten Namen tun, wenn Sie wollen.

Übung

Page 13: C# von Kopf bis Fuß

Sie sind hier 4 151

Typen und Referenzen

Erzeugen Sie einen Reisekostenerstattungsrechner für Geschäftsreisen. Der Benutzer sollte den Anfangs- und Endstand des Tachometers eingeben können. Auf Basis dieser beiden Zahlen wird der Rechner die angefallenen Kilometer berechnen und daraus ermitteln, wie hoch der Erstattungsbetrag ist, wenn für jeden angefallenen Kilometer 0,39 € gezahlt wird.

Beginnen Sie mit einem neuen Windows-Projekt.Geben Sie dem Formular folgende Gestalt:

1

Erzeugen Sie die Felder, die Sie für den Rechner benötigen.Stecken Sie die Felder in die Klassendefinition am Anfang von Form1. Sie brauchen zwei Felder für ganze Zahlen, die den Tachometer-Anfangsstand und -Endstand speichern. Nen-nen Sie diese tachoAnfangsstand und tachoEndstand. Außerdem brauchen Sie drei Zahlen, die Nachkommastellen aufnehmen können. Nehmen Sie jeweils den Typ double und nennen Sie sie gefahreneKM, erstattungsSatz und erstattungsBetrag. Setzen Sie den Wert für erstattungsSatz auf 0.39.

2

Der Bereich für diese Felder sollte 1 bis 999.999 umfassen.

Lassen Sie die Minimieren- und Maximieren-Buttons verschwinden.

Dieses Label ist 10 Punkte groß und fett.

Klicken Sie doppelt auf den Button, wenn Sie das Formular fertig haben, um dem Projekt etwas Code hinzuzufügen.

Bringen Sie den Rechner zum Funktionieren. Fügen Sie der Methode button1_Click() Code zu folgenden Zwecken hinzu:

≥ Stellen Sie sicher, dass die Zahl im Feld tachoAnfangsstand kleiner als die Zahl im Feld tachoEndstand ist. Lassen Sie andernfalls ein Dialogfenster mit der Mel-dung »Der Anfangsstand muss kleiner als der Endstand sein.« anzeigen. Geben Sie diesem Dialog den Titel »Keine Berechnung möglich«.

≥ Ziehen Sie mit folgenden Zeilen den Anfangsstand vom Endstand ab und multipli-zieren Sie ihn dann mit dem Erstattungssatz:

gefahreneKM = tachoEndstand -= tachoAnfangsstand;

erstattungsBetrag = gefahreneKM *= erstattungsSatz;

label4.Text = erstattungsBetrag + "€";

3

Führen Sie die Anwendung aus.Prüfen Sie, ob der Rechner korrekte Werte liefert. Probieren Sie, den Anfangsstand größer als den Endstand zu machen, und schauen Sie, ob Ihr Dialogfenster angezeigt wird.

4

Übung

Page 14: C# von Kopf bis Fuß

152 Kapitel 4

Hier ist der Code für den ersten Teil der Übung.

Etwas stimmt nicht …

public partial class Form1 : Form

{

int tachoAnfangsstand;

int tachoEndstand;

double gefahreneKM;

double erstattungsSatz = 0.39;

double erstattungsBetrag;

public Form1() {

InitializeComponent();

}

private void button1_Click(object sender, EventArgs e){

tachoAnfangsstand = (int)numericUpDown1.Value;

tachoEndstand = (int)numericUpDown2.Value;

if (tachoAnfangsstand <= tachoEndstand){

gefahreneKM = tachoEndstand -= tachoAnfangsstand;

erstattungsBetrag = gefahreneKM *= erstattungsSatz;

label4.Text = erstattungsBetrag + "€";

} else {

MessageBox.Show(

"Der Anfangsstand muss kleiner als der Endstand sein.",

"Keine Berechnung möglich");

} } }

Haben Sie daran gedacht, den decimal-Wert des NumericUpDown-Steuer-elements in einen int umzuwandeln?

Ja, der Punkt ist richtig. Das und nicht das Komma ist der Dezimaltrenner, den C# verwendet - genau wie jede andere Programmiersprache. Darauf müssen Sie achten, wenn Sie in Ihrem Code Gleit-kommazahlen literal angeben.

int funktioniert wunderbar für ganze Zahlen. Diese Zahl könnte bis 999.999 gehen. Ein short oder ein byte würde also nicht reichen.

Dieser Block soll berechnen, wie viele Kilometer zurückgelegt wurden, und diese dann mit dem Erstattungssatz multiplizieren.

Dieser Button scheint zu funktionieren, birgt eigentlich aber ein ziemlich großes Problem. Sehen Sie es?

Wir haben hier eine alternative Form der Methode MessageBox.Show() verwendet. Wir haben ihr zwei Parameter gegeben, der erste ist die anzuzeigende Nachricht und der zweite der Text der Titelleiste.

lösung zur Übung

Page 15: C# von Kopf bis Fuß

Sie sind hier 4 153

Typen und Referenzen

Fügen Sie dem Formular einen weiteren Button hinzu. Gehen wir dem Problem auf den Grund, indem wir dem Formular einen Button hinzufügen, der den Wert des Felds gefahreneKM anzeigt (dazu könnten Sie auch den Debugger nutzen).

1

Eine Zeile Code sollte Reichen. Wir müssen das Formular im Prinzip nur dazu bringen, die Variable gefahreneKM anzuzeigen, oder? Das sollte sich eigentlich mit dieser Zeile erledigen lassen:

private void button2_Click(object sender, EventArgs e) {

Messagebox.Show(gefahreneKM + " KM",

"Zurückgelegte Kilometer");

}

2

Klicken Sie, wenn Sie das Formular fertig haben, doppelt auf den Button Kilometer anzeigen, um dem Projekt etwas Code hinzuzufügen.

Führen Sie Es aus. Geben Sie ein paar Werte ein und schauen Sie, was passiert. Geben Sie zunächst einen Anfangsstand und einen Endstand ein und klicken Sie auf den Berechnen-Button. Klicken Sie dann auf den Button »Kilome-ter anzeigen«, um zu sehen, was im Feld gefahreneKM gespeichert ist.

3

Hmm, da stimmt was nicht … Ganz gleich, was Sie tun, die Kilometeranzahl entspricht immer dem Erstattungsbetrag. Warum?

4

Nach einem Klick auf Berechnen soll dieser Button dazu verwendet werden können, in einem Dialogfenster die zurückgelegten Kilometer anzuzeigen.

Tun Sie dasDen Kilometerrechner debuggenIrgendetwas mit dem Kilometerrechner stimmt nicht. Wenn Ihr Code nicht auf die erwartete Weise funktioniert, gibt es immer einen Grund dafür. Sie sind dafür verantwortlich, diesen Grund zu fin-den. Untersuchen wir, was schiefgelaufen ist, und schauen wir, ob wir es reparieren können.

Page 16: C# von Kopf bis Fuß

154 Kapitel 4

private void button1_Click(object sender, EventArgs e)

{

tachoAnfangsstand = (int)numericUpDown1.Value;

tachoEndstand = (int)numericUpDown2.Value;

if (tachoAnfangsstand <= tachoEndstand){

gefahreneKM = tachoEndstand -= tachoAnfangsstand;

erstattungsBetrag = gefahreneKM *= erstattungsSatz;

label4.Text = erstattungsBetrag + "€";

} else {

MessageBox.Show(

"Der Anfangsstand muss kleiner als der Endstand sein.",

"Keine Berechnung möglich");

} }

Operatoren in der Warteschleife

Sehen Sie sich einmal genau den Operator an, den wir verwendet haben, um den Tachometer-Anfangsstand vom Tachometer-Endstand abzuziehen (–=). Das Problem ist, dass er nicht nur subtrahiert, sondern der Variablen links des Minuszeichens auch einen Wert zuweist. Das Gleiche passiert in der Zeile, in der wir die Anzahl der zurückgelegten Kilometer mit der Erstat-tungsrate multiplizieren. Wir müssen –= und *= lediglich durch – bzw. * ersetzen.

Einen Operator mit = kombinieren

Das nennt man einen zusammengesetzten Operator. Er zieht tachoAnfangsstand von tachoEnd-stand ab, weist den neuen Wert aber auch gleichzeitig tacho Endstand und gefahreneKM zu.

Können Ihnen gute Variablen hier helfen? Unbedingt! Sehen Sie sich genau an, was die einzelnen Variablen vermeintlich tun. Der Name gefahreneKM gibt Ihnen bereits eine Menge Hinweise – Sie wissen, dass es diese Variable ist, die das Formular falsch anzeigt, und haben eine gute Vorstellung davon, wie dieser Wert berechnet werden sollte. Das können Sie also nutzen, wenn Sie Ihren Code durchsuchen, um den Fehler aufzuspüren. Es wäre viel schwerer, das Problem zu finden, sähen die fehlerhaften Zeilen stattdessen so aus:

mT = eM -= sM;

aO = mT *= rR;

gefahreneKM = tachoEndstand - tachoAnfangsstand;

erstattungsBetrag = gefahreneKM * erstattungsSatz;

Das ist besser – jetzt verändert der Code tachoEndstand und gefahreneKM nicht mehr.

Variablen mit Namen wie diese sind im Grunde nutzlos, wenn es darum geht, Ihnen etwas über ihren Zweck mitzuteilen.

Page 17: C# von Kopf bis Fuß

Sie sind hier 4 155

Typen und Referenzen

Auch für Objekte braucht man VariablenBislang haben wir Objekte von den anderen Typen getrennt betrachtet. Aber Objekte sind einfach nur eine andere Art von Daten, die Ihr Code genau so behandelt wie Zahlen, Strings und Boolesche Werte. Er nutzt Variablen, um mit ihnen zu arbeiten.

Einen int verwenden Ein Objekt verwenden

Schreiben Sie eine Anweisung, die ein Objekt deklariert.

Hund rex;

1Schreiben Sie eine Anweisung, die einen int deklariert.

int einInt;

1

Weisen Sie der Variablen ein Objekt zu.

rex = new Hund();

2Weisen Sie der Variablen einen Wert zu.

einInt = 3761;

2

Prüfen Sie eins der Felder des Objekts.

while (rex.Zufrieden) {

3Nutzen Sie den Wert in Ihrem Code.

while (i < einInt) {

3

Haben Sie eine Klasse wie Hund, nutzen Sie diese in der Variablendeklaration als Typ.

Ein Objekt ist einfach eine weitere Art von Variable, die Ihr Programm verwenden kann.Muss Ihr Programm mit einer richtig großen ganzen Zahl arbeiten, verwenden Sie einen long, und muss es mit einer kleinen ganzen Zahl arbeiten, nehmen Sie einen short. Braucht es einen Ja/Nein-Wert, verwenden Sie einen boolean. Benötigt es jedoch etwas, das bellt und Sitz macht, verwenden Sie einen Hund. Ganz gleich, mit Daten welcher Art Ihr Programm arbeiten muss, es verwendet immer eine Variable.

Es spielt also keine Rolle, ob ich mit ei-nem Objekt oder einem numerischen Wert

arbeite. Ich verwende in beiden Fällen eine Variable, wenn ich etwas zur späteren

Verwendung speichern muss.

Page 18: C# von Kopf bis Fuß

156 Kapitel 4

Typ-Objekt 1

Die Referenz erhalten

Um ein neues Objekt zu erstellen, verwenden Sie Code wie new Typ. Aber das ist nicht genug. Obgleich dieser Code ein neues Typ-Objekt auf dem Heap erstellt, gibt er Ihnen noch keine Möglichkeit, auf dieses Objekt zuzugreifen. Sie benötigen eine Referenz auf dieses Objekt. Sie erzeugen deswegen eine Referenzvariable: eine Variable des Typs Typ mit einem Namen wie tim. tim ist also eine Referenz auf das neue Typ-Objekt, das Sie erstellt haben. Jedes Mal, wenn Sie diesen bestimmten Typ verwenden möchten, können Sie ihn mit der Referenzvariablen namens tim referenzieren.

Wenn Sie eine Variable haben, die einen Objekttyp hat, ist es eine Referenzvariable: eine Referenz auf ein bestimmtes Objekt. Werfen Sie einen Blick darauf:

Mit Referenzvariablen auf Objekte verweisen

public partial class Form1 : Form{ Typ tim;

public Form1() { InitializeComponent();

tim = new Typ(); }

Das wird als Instantiierung des Objekts bezeichnet.

Hier ist der Heap vor der Ausführung Ihres Codes.

Hier ist der Heap nach Ausführung Ihres Codes. Es gibt ein Objekt, auf das die Variable tim verweist.

Diese Variable heißt tim, sie wird ein Objekt des Typs Typ referenzieren.

Das ist die Referenz-variable …

… und das ist das Objekt, auf das tim jetzt verweist.

Dieses Typ-Objekt kann NUR über die Referenzvariable tim referenziert werden.

Das Erstellen einer Referenz ist, als erstelle man ein Etikett mit einer Etikettiermaschine – statt es an Ihr Zeug zu kleben, nutzen Sie es, um ein Objekt zu etikettieren, damit Sie später darauf verweisen können.

Page 19: C# von Kopf bis Fuß

Sie sind hier 4 157

Typen und Referenzen

In der Küche haben Sie wahrscheinlich eine Dose für Salz und eine für Zucker. Würden Sie die Etiketten darauf tauschen, könnte das dazu führen, dass Sie das eine oder andere Essen vergeigen – den selbst wenn die Etiketten vertauscht werden, bleibt der Inhalt gleich. Referenzen sind wie Etiketten. Sie können Etiketten herumreichen, an unterschiedliche Dinge heften, aber das Objekt stellt die Methoden und Daten, nicht die Referenz selbst.

Referenzen sind wie Et iketten für Ihr Objekt

Das ist ein Objekt des Typs Typ. Es ist ein EINZELNES Objekt, auf das VIELE Referenzen zeigen.

Auf ein Objekt verweist man nie direkt. Code wie Typ.GeldGeben() können Sie beispielsweise nicht schreiben, wenn Typ Ihr Objekttyp ist. Der C#-Compiler weiß nicht, von welchem Typ Sie hier reden, da es auf dem Heap mehrere Instan-zen von Typ geben könnte. Sie brauchen also eine Referenzvariable tim, der Sie eine spezifische Instanz zuweisen können, z. B. Typ tim = new Typ().

Jetzt können Sie (nicht statische) Methoden wie tim.GeldGeben() verwenden. tim verweist auf eine bestimmte Instanz der Klasse Typ, und der C#-Compiler weiß genau, welche Instanz er verwenden soll. Wie Sie oben gesehen haben, können mehrere Etiketten auf die gleiche Instanz zeigen. Sie könnten also Typ papa = tim sagen und dann papa.GeldGeben() aufrufen. Auch das geht – Tims Sohn macht das jeden Tag!

Die button1_Click-Methode von Form1 referenziert dieses Objekt über eine Variable mit dem Namen »tim«

Es gibt viele verschiedene Referenzen auf dieses Objekt, weil viele verschie-dene Methoden es für unterschiedli-che Dinge verwenden. Jede Referenz hat einen anderen Namen, der in ihrem jeweiligen Kontext sinnvoll ist.

Jedes dieser Etiketten ist eine Referenzvariable. All diese Variablen zeigen aber auf das GLEICHE Typ-Objekt.

Muss Ihr Code mit Objekten im Speicher arbeiten, nutzt er eine Referenz, d. h. eine Variable, deren Typ die Klasse des Objekts ist, auf das sie verweist. Eine Referenz ist wie ein Etikett, das Ihr Code nutzt, um mit einem bestimmten Objekt zu reden.

Eine andere Klasse verweist auf diese Instanz der Klasse Typ über eine Variable namens »papa«.

Typ papa = tim;

Page 20: C# von Kopf bis Fuß

158 Kapitel 4

Typ-Objekt

Typ-Objekt 2 Typ-Objekt 1

Typ-Objekt 2

Trägt ein Objekt kein einziges Etikett mehr, können Programme nicht mehr auf dieses Objekt zugreifen. Das heißt, dass C# dieses Objekt für die Müllabfuhr, die wir, wie in Programmiererkreisen üblich, Garbage Col-lection nennen werden, vormerkt. So bezeichnet man den Mechanismus, über den C# nicht referenzierte Objekte loswird und den Speicher freigibt, den diese Objekte für Ihr Programm belegt haben.

Ein Objekt bleibt nur so lange auf dem Heap, wie es Referenzen da­rauf gibt. Ver­schwindet die letzte Referenz, verschwindet auch das Objekt.

Gibt es keine Referenzen mehr, holt die Müllabfuhr Ihr Objekt

Wenn Sie die »new«-Anweisung nutzen, weisen Sie C# an, ein Objekt zu erstellen. Nehmen Sie eine Referenzvariable wie tim und weisen ihr dieses Objekt zu, ist das, als würden Sie ein neues Label an das Objekt heften.

Hier ist etwas Code, der ein Objekt erzeugt.

Typ tim = new Typ()

{ Name = "Tim", Geld = 50 };

1

Erzeugen wir jetzt ein zweites Objekt.

Typ tom = new Typ()

{ Name = "Tom", Geld = 75 };

2

Ändern wir die Referenz auf das erste Objekt und las-sen wir sie auf das zweite zeigen.

tim = tom;

3

Jetzt zeigt tim auf das gleiche Objekt wie tom.

Vielen Dank, Herr Kammerjäger

Jetzt haben wir zwei Typ-Instanzen und zwei Referenzvariablen: eine für jede Typ-Instanz.

Aber es gibt keine Referenz auf das erste Typ-Objekt mehr …

… C# merkt es des-wegen für die Garbage Collection vor, die es irgendwann löscht. Und schwups, weg ist es!

»Tim«50

»Tom«75

»Tim«50

»Tom«75

Page 21: C# von Kopf bis Fuß

Sie sind hier 4 159

Typen und Referenzen

Typ-Kreuz-worträtselMachen Sie eine Pause, lehnen Sie sich zurück und geben Sie Ihrer rechten Gehirnhälfte etwas zu tun. Und zwar wieder mit dem Ihnen schon bekannten Kreuzworträtsel. Alle Lösungswörter stammen aus diesem Kapitel.

Wenn Sie fertig sind, können Sie umblättern, um sich dem Rest dieses Kapitels zu widmen.

Antworten auf Seite 183.

1 2

3 4

5 6

7

8 9 10 11

12

13

14 15

16

Waagerecht3. Der Typ, der die größten Zahlen speichern kann.7. \n und \r sind _____-Sequenzen.8. Wird das für eine Variable nicht gesetzt, lässt sich Ihr Programm nicht kompilieren.10. Die Deklaration einer Variablen beginnt immer damit.12. Der Typ, der nur ein einzelnes Zeichen speichert.13. Jedes Objekt hat diese Methode, die es in einen String umwandelt.14. Das tun Sie, wenn Sie mit dem +-Operator zwei Strings zusammenkleben.15. Die Deklaration und die ____________ einer Variablen können zu einer Anweisung zusammengefasst werden.16. Zeigen auf ein Objekt keine Referenzen mehr, wird es von der ____________-Collection aus dem Heap entfernt.

Senkrecht1. »namespace«, »for«, »while«, »using« und »new« sind alles Beispiele für _____________ Wörter.2. Das machen die arithmetischen Operatoren für Sie mit Typen automatisch.4. Einer Variablen dieses Typs können Sie jeden Wert zuweisen.5. Der zweite Teil einer Variablendeklaration.6. Was Ihr Programm nutzt, um mit Daten zu arbeiten, die im Speicher sind.9. Eine Variable, die auf ein Objekt zeigt.11. Die vier Typen für ganze Zahlen, die nur positive Zahlen speichern.12. Das, was (int) in folgender Codezeile macht: x = (int) y;

1 2

3 4

5 6

7

8 9 10 11

12

13

14 15

16

Waagerecht3. Der Typ, der die größten Zahlen speichern kann.7. \n und \r sind _____-Sequenzen.8. Wird das für eine Variable nicht gesetzt, lässt sich Ihr Programm nicht kompilieren.10. Die Deklaration einer Variablen beginnt immer damit.12. Der Typ, der nur ein einzelnes Zeichen speichert.13. Jedes Objekt hat diese Methode, die es in einen String umwandelt.14. Das tun Sie, wenn Sie mit dem +-Operator zwei Strings zusammenkleben.15. Die Deklaration und die ____________ einer Variablen können zu einer Anweisung zusammengefasst werden.16. Zeigen auf ein Objekt keine Referenzen mehr, wird es von der ____________-Collection aus dem Heap entfernt.

Senkrecht1. »namespace«, »for«, »while«, »using« und »new« sind alles Beispiele für _____________ Wörter.2. Das machen die arithmetischen Operatoren für Sie mit Typen automatisch.4. Einer Variablen dieses Typs können Sie jeden Wert zuweisen.5. Der zweite Teil einer Variablendeklaration.6. Was Ihr Programm nutzt, um mit Daten zu arbeiten, die im Speicher sind.9. Eine Variable, die auf ein Objekt zeigt.11. Die vier Typen für ganze Zahlen, die nur positive Zahlen speichern.12. Das, was (int) in folgender Codezeile macht: x = (int) y;

1 2

3 4

5 6

7

8 9 10 11

12

13

14 15

16

Waagerecht3. Der Typ, der die größten Zahlen speichern kann.7. \n und \r sind _____-Sequenzen.8. Wird das für eine Variable nicht gesetzt, lässt sich Ihr Programm nicht kompilieren.10. Die Deklaration einer Variablen beginnt immer damit.12. Der Typ, der nur ein einzelnes Zeichen speichert.13. Jedes Objekt hat diese Methode, die es in einen String umwandelt.14. Das tun Sie, wenn Sie mit dem +-Operator zwei Strings zusammenkleben.15. Die Deklaration und die ____________ einer Variablen können zu einer Anweisung zusammengefasst werden.16. Zeigen auf ein Objekt keine Referenzen mehr, wird es von der ____________-Collection aus dem Heap entfernt.

Senkrecht1. »namespace«, »for«, »while«, »using« und »new« sind alles Beispiele für _____________ Wörter.2. Das machen die arithmetischen Operatoren für Sie mit Typen automatisch.4. Einer Variablen dieses Typs können Sie jeden Wert zuweisen.5. Der zweite Teil einer Variablendeklaration.6. Was Ihr Programm nutzt, um mit Daten zu arbeiten, die im Speicher sind.9. Eine Variable, die auf ein Objekt zeigt.11. Die vier Typen für ganze Zahlen, die nur positive Zahlen speichern.12. Das, was (int) in folgender Codezeile macht: x = (int) y;

Page 22: C# von Kopf bis Fuß

160 Kapitel 4

Hund-Objekt 1

Hund-Objekt 3

Hund-Objekt 1

Hund-Objekt 2

So viele Etiketten

Wenn Sie damit beginnen, Ihre Referenzvariablen herumzureichen, müssen Sie aufpassen. Oft scheint es, als würden Sie eine Variable einfach auf ein anderes Objekt zeigen lassen. Aber das kann dazu führen, dass dabei alle Referenzen auf ein Objekt gelöscht werden. Das ist nicht notwendigerweise falsch, aber auch nicht immer das, was Sie eigentlich wollten. Werfen Sie einen Blick darauf:

Mehrere Referenzen und ihre Nebenwirkungen

Hund-Objekt 1

Hund hasso = new Hund();

hasso.Rasse = "Windhund";1

Hund fido = new Hund();

fido.Rasse = "Beagle";

Hund rex = hasso;

2

Hund lucky = new Hund();

lucky.Rasse = "Dackel";

fido = hasso;

3

hasso ist ein Hund-Objekt mit dem Rasse-Wert Windhund.

fido ist ein weiteres Hund-Objekt. rex dagegen ist nur eine weitere Referenz auf das erste Objekt.

lucky ist das dritte Objekt. Aber fido zeigt jetzt auf Objekt 1. Es gibt keine Referenzen auf Objekt 2 mehr. Für das Programm ist es erledigt.

Objekte:______

Referenzen:_____

Objekte:______

Referenzen:_____

Objekte:______ Referenzen:_____

11

23

24

Page 23: C# von Kopf bis Fuß

Sie sind hier 4 161

Typen und Referenzen

Hund hasso = new Hund();hasso.Rasse = "Windhund";Hund rantanplan = new Hund();Hund fido = new Hund();Hund quentin = fido;

1

Hund rex = new Hund();rex.Rasse = "Dackel";rex = hasso;

2

Hund lucky = new Hund();lucky.Rasse = "Beagle";Hund charlie = fido;fido = hasso;

3

Objekte:______

Referenzen:_____

Objekte:______ Referenzen:_____

rantanplan = lucky;Hund otto = new Hund();otto.Rasse = "Mops";

4

Objekte:______ Referenzen:_____

charlie = otto;lucky = rantanplan;

5

Objekte:______ Referenzen:_____

Objekte:______

Referenzen:_____

Jetzt sind Sie an der Reihe. Hier ist ein langer Codeblock. Ermitteln Sie, wie viele Objekte und Referenzen es nach den einzelnen Phasen gibt. Zeichnen Sie auf der rechten Seite ein Bild mit den Objekten und Etiket-ten auf dem Heap.

Spitzen Sie Ihren Bleistift

Page 24: C# von Kopf bis Fuß

162 Kapitel 4

Hund-Objek t

5

Hund-Objekt

3

Hund-Objekt

1

Hund-Objekt

4

Hund-Objekt

1

Hund-Objekt

3

Hund-Objekt

5

Hund-Objekt

4

Hund-Objekt

2Hund-Objek

t 3

Hund-Objekt

1

Hund-Objekt

4

Hund-Objekt

3

Hund-Objekt

1

Hund-Objek t

2

Hund-Objekt

3

Typen und Referenzen

Hund hasso = new Hund();hasso.Rasse = "Windhund";Hund rantanplan = new Hund();Hund fido = new Hund();Hund quentin = fido;

1

Hund rex = new Hund();rex.Rasse = "Dackel";rex = hasso;

2

Hund lucky = new Hund();lucky.Rasse = "Beagle";Hund charlie = fido;fido = hasso;

3

Objekte:______

Referenzen:_____

Objekte:______ Referenzen:_____

rantanplan = lucky;Hund otto = new Hund();otto.Rasse = "Mops";

4

Objekte:______ Referenzen:_____

charlie = otto;lucky = rantanplan;

5

Objekte:______ Referenzen:_____

Objekte:______

Referenzen:_____

Jetzt sind Sie an der Reihe. Hier ist ein langer Codeblock. Sie sollten ermitteln, wie viele Objekte und Referenzen es nach den einzelnen Phasen gibt, und auf der rechten Seite ein Bild mit den Objekten und Etiketten auf dem Heap zeichnen.

3

3

47

48

4 8

Es wird ein neues Hund-Objekt erzeugt, aber rex ist die einzige Referenz darauf. Nach rex = hasso ist dieses Objekt wieder verschwunden.

Hier wird ein neues Hund-Objekt erstellt, das erhalten bleibt, weil die Referenz darauf nicht überschrieben wird.

charlie wurde gleich fido gesetzt, als fido noch auf Objekt 3 verwies. Nachdem fido gleich hasso gesetzt wurde, verweist fido auf Objekt 1, charlie hingegen weiter auf Objekt 3.

Hier werden nur Referenzen verschoben, aber keine neuen Objekte erzeugt. lucky gleich rantanplan zu setzen, änderte nichts, da beide Variablen bereits auf das gleiche Objekt zeigen.

4

5

Hund-Objekt

1

Hund-Objekt

2

Objekt 2 ver-liert seine letzte Referenz und verschwindet.

Weil lucky jetzt auf das gleiche Objekt wie rantanplan verweist, gibt es keine Referenz auf das alte Objekt von rantanplan mehr.

LösungSpitzen Sie Ihren Bleistift

Page 25: C# von Kopf bis Fuß

Sie sind hier 4 163

Typen und Referenzen

Erstellen Sie ein Programm mit einer Elefant-Klasse. Erzeugen Sie zwei Elefant-Instanzen und tauschen Sie dann die Referenzvariablen, die auf sie zeigen, ohne dass eine der Elefant-Instanzen dabei ein Fall für die Müllabfuhr wird.

Erstellen Sie ein neues Windows Forms-Anwendung-Projekt.Geben Sie dem Formular diese Gestalt:

1

Erzeugen Sie die Klasse Elefant.Fügen Sie dem Projekt die Klasse Elefant hinzu. Sehen Sie sich das Klassendiagramm für Elefant an – Sie brauchen ein int-Feld namens Ohrengröße und ein string-Feld namens Name. (Achten Sie darauf, dass beide public sind.) Fügen Sie dann die Methode WerBinIch() hinzu, die ein Dialogfens-ter anzeigt, das Ihnen den Namen und die Länge des Elefantenohrs sagt.

2

Erzeugen Sie zwei Elefant-Instanzen und -Referenzen. Fügen Sie Form1 (direkt unter der Klassendeklaration) zwei Elefant-Felder namens kunibert und kunigunde hinzu. Initialisieren Sie diese mit dem korrekten Namen und einer Ohrengröße. Hier sind die Elefant-Objektinitialisierer für Ihr Formular:

kunigunde = new Elefant() { Name = "Kunigunde", Ohrengröße = 63 }; kunibert = new Elefant() { Name = "Kunibert", Ohrengröße = 81 };

3

Machen Sie die Buttons »Kunibert« und »Kunigunde« funktionsfähig.Lassen Sie den Kunibert-Button kunibert.WerBinIch() und den Kunigunde-Button kunigunde.WerBinIch() aufrufen.

4

Richten Sie den Tauschen-Button ein. Jetzt kommt der schwere Teil. Bringen Sie den Tauschen-Button dazu, die beiden Referenzen zu tauschen, damit die Variablen kunibert und kunigunde durch einen Klick auf Tauschen die Objekte tauschen und eine »Objekte getauscht«-Meldung angezeigt wird. Testen Sie das Programm, indem Sie auf Tauschen und dann auf die beiden anderen Buttons klicken. Nach dem ersten Klick auf Tauschen sollte der Button Kunibert das Dialogfenster für Kunigunde anzeigen. Nach dem nächs-ten Klick auf Tauschen sollte alles wieder beim Alten sein.

5

ElefantNameOhrengröße

WerBinIch()

Ein Klick auf den Button »Kunigunde« ruft

kunigunde.WerBinIch() auf. Diese Methode

zeigt dieses Dialogfenster an.

C# löscht alle Objekte, auf die es keine Referenzen mehr gibt. Ein kleiner Hinweis: Wenn Sie ein Glas Bier in ein Glas schütten wollen, in dem im Moment Wasser ist, brauchen Sie ein drittes Glas ...

Hier ist das Klassendiagramm für die zu erstellende Klasse Elefant.

Die Methode WerBinIch() sollte diesen Dialog öffnen. Prüfen Sie, ob der Text die Ohrengröße enthält und in der Titelleiste der Name steht.

Übung

Page 26: C# von Kopf bis Fuß

164 Kapitel 4

Erstellen Sie ein Programm mit einer Elefant-Klasse. Sie erzeugen Sie zwei Elefant-Instanzen und tauschen dann die Referenzvariablen, die auf sie zeigen, ohne dass eine der Elefant-Instanzen dabei ein Fall für die Müllabfuhr wird.

Warum haben wir die Methode Tauschen() nicht in die Klasse Elefant eingefügt?

using System.Windows.Forms;

class Elefant {

public int Ohrengröße; public String Name;

public void WerBinIch() { MessageBox.Show("Meine Ohren sind " + Ohrengröße + " cm lang.", Name + " sagt"); }} public partial class Form1 : Form {

Elefant kunigunde; Elefant kunibert;

public Form1() { InitializeComponent(); kunigunde = new Elefant() { Name = "Kunigunde", Ohrengröße = 63 }; kunibert = new Elefant() { Name = "Kunibert", Ohrengröße = 81 }; }

private void button1_Click(object sender, EventArgs e) { kunibert.WerBinIch(); }

private void button2_Click(object sender, EventArgs e) { kunigunde.WerBinIch(); }

private void button3_Click(object sender, EventArgs e) { Elefant halter; halter = kunibert; kunibert = kunigunde; kunigunde = halter; MessageBox.Show("Objekte getauscht"); }}

Für diese Referenz gibt es keine new-Anweisung, weil wir keine weitere Elefant-Instanz brauchen.

Lassen Sie einfach kuni-bert auf kunigunde zeigen, gibt es keine Referenz auf Kunibert mehr. Sein Objekt geht also verloren. Deswegen brauchen Sie die Referenzhalter, um das Kunibert-Objekt festzu-halten, wenn der Referenz kunibert das Objekt Kuni-gunde zu gewiesen wird.

Das ist der Code für die Klasse Elefant in der Datei Elefant.cs, die wir dem Projekt hinzugefügt haben. Vergessen Sie am Anfang des Formulars die »using System.Windows.Forms;«-Zeile nicht. Ohne funktioniert die MessageBox-Anwei-sung nicht.

Hier ist der Code für die Klasse Form1 aus Form1.cs.

Halten Sie Ihre Referenzen

Sie können diese using-Anweisung in die geschweiften Klammern des Na-mensraums stecken, wenn Sie wollen.

lösung zur Übung

Kopf-nuss

Page 27: C# von Kopf bis Fuß

Sie sind hier 4 165

Typen und Referenzen

Zwei Referenzen bedeuten ZWEI Möglichkeiten, die Daten e ines Objekts zu ändern

Elefant-Objek

t

Elefant-Objek

t

private void button4_Click(object sender, EventArgs e){ kunibert = kunigunde; kunibert.Ohrengröße = 4321; kunibert.WerBinIch();}

Sie können nicht nur alle Referenzen auf ein Objekt ver-lieren, wenn Sie mehrere Referenzen haben, Sie können ein Objekt auch unbeabsichtigt ändern. Anders gesagt: Eine Referenz auf ein Objekt kann das Objekt ändern, während die andere Referenz auf das Objekt keine Ahnung hat, das sich etwas geändert hat. Aufgepasst:

Spendieren Sie dem Formular noch einen Button.1

Das ist der Code für diesen Button. Können Sie sich denken, was passiert, wenn Sie darauf klicken?2

Klicken Sie dann auf den neuen Button. Moment mal ... das ist doch die Meldung für Kunigunde! Haben wir nicht die WerBinIch()-Methode von Kunibert aufgerufen?

3

Sie rufen die Methode

WerBinIch() auf der Referenz kunibert a

uf.

Das ist die Meldung für Kunigunde.

Aber die Ohrengröße haben wir doch per Referenz kunibert gesetzt! Wieso das?

Nachdem dieser Code ausgeführt wurde, zeigen kunibert und kunigunde auf das GLEICHE Objekt.

kunibert und kunigunde sind jetzt austauschbar. Än-

derungen an einer der Referenzen ändern das Objekt,

auf das BEIDE zeigen … es gibt keinen Unterschied

zwischen kunibert und kunigunde mehr, da beide auf das

GLEICHE Objekt zeigen.

Aber kunibert zeigt auf das gleiche Ding wie kunigunde.

Diese Anweisung setzt die Ohren-größe des Objekts, auf das kunibert verweist, auf 4321.

Beachten Sie, dass die Daten NICHT über-schrieben werden – es ändern sich nur die Referenzen.

Tun Sie das!

Page 28: C# von Kopf bis Fuß

166 Kapitel 4

Array

Eines für viele

Ein Sonderfall: Arrays

int[] größe;

größe = new int[7];

größe[0] = 68;

größe[1] = 70;

größe[2] = 63;

größe[3] = 60;

größe[4] = 58;

größe[5] = 72;

größe[6] = 74;

Name Der Typ jedes Elements in diesem Array.

Beachten Sie, dass das Array ein Objekt ist, obwohl die 7 Elemente Werttypen sind – wie die auf den ersten beiden Seiten dieses Kapitels.

7 int-Variablen

Im Speicher wird das Array

als ein Speicherhappen gespei-

chert, obwohl darin mehrere

int-Variablen stecken.

Wenn Sie eine Menge Daten des gleichen Typs festhalten müssen, beispiels-weise eine Liste mit Größen oder eine Rotte von Hunden, können Sie das mit einem Array machen. Ein Array zeichnet aus, dass es eine Sammlung von Variablen ist, die als ein Objekt behandelt wird. Über ein Array können Sie mehrere Datenteile speichern und ändern, ohne dass Sie die einzelnen Variab-len separat nachhalten müssen. Um ein Array zu erzeugen, deklarieren Sie es genau wie jede andere Variable mit einem Namen und einem Typ:

bool[] einArray;

einArray = new bool[15];

einArray[4] = true;

Sie deklarieren ein Array über den Typ plus eckige Klammern.

Nutzen Sie die Elemente in e inem Array, als wären es gewöhnliche VariablenWenn Sie ein Array nutzen, müssen Sie zunächst eine Referenzvariable de-klarieren, die auf das Array zeigt. Dann müssen Sie mit new das Array-Objekt erstellen und dabei angeben, wie groß das Array sein soll. Erst dann können Sie die Elemente im Array setzen. Hier ist ein Codebeispiel, das ein Array dekla-riert und füllt – und daneben sehen Sie die Abbildung, die zeigt, was dabei auf dem Heap passiert. Das erste Element des Arrays hat den Index null.

Sie hätten die Deklaration der Variablen einArray mit der Initialisierung verbinden können – wie bei jeder anderen Variablen. Dann sähe das so aus:bool[] einArray = new bool[15];

Ein Array wird mit dem Schlüssel-wort new erzeugt, weil es ein Objekt ist. Eine Array-Variable ist also ebenfalls eine Referenz variable. Diese Zeile setzt den Wert des fünften Elements von einArray auf true. Es ist das fünfte, weil das erste einArray[0] ist, das zweite einArray[1] usw.

Dieses Array enthält 15 Elemente.

Sie referen-zieren die Elemente, aber jedes funktioniert im Prinzip wie eine gewöhnli-che Variable.

int int int int int int int

Strings und Arrays unterscheiden sich von den anderen Datenty-pen, die Ihnen bisher begegnet

sind. Sie sind die einzigen, deren Größe nicht festgelegt ist (denken

Sie eine Weile darüber nach).

Page 29: C# von Kopf bis Fuß

Sie sind hier 4 167

Typen und Referenzen

Hund-Objek

tHund-Obj

e kt

Ein Array mit Objektreferenzen können Sie auf gleiche Weise erzeugen wie ein Array mit Zahlen oder Strings. Arrays kümmern sich nicht um den Typ der darin gespeicherten Variablen. Der bleibt Ihnen überlassen. Sie können also problemlos ein Array mit ints wie auch ein Array mit Ente-Objekten haben.

Hier ist Code, der ein Array mit 7 Hund-Variablen erzeugt. Die Zeile, die ledig-lich das Array initialisiert, erzeugt auch die Referenzvariablen. Da es nur zwei new Hund()-Zeilen gibt, werden auch nur zwei richtige Instanzen der Klasse Hund erstellt.

Hund[] hunde = new Hund[7];

hunde[5] = new Hund();

hunde[0] = new Hund();

Arrays können auch e inen Haufen Referenzvariablen speichern

Diese Zeile deklariert die Variable hunde, die ein Array mit Referenzen auf Hund-Objekte speichert, und erzeugt dann ein Array mit 7 Elementen.

Diese beiden Zeilen erzeugen neue Instanzen von Hund und stecken sie zu den Indizes 0 und 5.

Die beim Setzen oder Abrufen eines Array­Elements in den eckigen Klammern angegebene Zahl nennt man Index. Das erste Element im Array hat den Index null.

Alle Elemente in diesem Array sind Referenzen. Das Array selbst ist ein Objekt.

7 Hund-Variablen

Die erste Zeile des Codes hat nur das

Array erzeugt, nicht die Instanzen.

Das Array ist eine Liste mit 7 Hund-

Referenzvariablen.

Hund Hund Hund Hund Hund Hund Hund

Array

Die Länge von ArraysWie viele Elemente in einem Array

stecken, können Sie über seine Length-Eigenschaft ermitteln. Haben

Sie ein Array namens größe, können

Sie größe.Length verwenden, um seine

Länge zu ermitteln. Enthält es 7

Elemente, wird Ihnen 7 gemeldet –

das bedeutet, dass die Elemente im

Array die Indizes 0 bis 6 tragen.

Page 30: C# von Kopf bis Fuß

168 Kapitel 4

Willkommen beim Sandwich-Discount vom Strammen Max Der Stramme Max hat einen Haufen Fleisch, diverses Brot und mehr Soßen, als man aufzählen kann. Aber was er nicht hat, ist eine Speisekarte! Können Sie ein Programm erstellen, das ihm jeden Tag eine neue zufällige Speisekarte erstellt?

Der Stramme Max sagt: »Es ist nicht alt, es ist reif.«

Fügen Sie einem neuen Projekt die Klasse MenüMacher hinzu.Zur Herstellung einer Speisekarte benötigen Sie Zutaten. Arrays wären perfekt für diese Listen. Außerdem brauchen wir eine Möglichkeit, zufällig Zutaten auszu-wählen, um diese zu einem Sandwich zu kombinieren. Das .NET Framework bietet die eingebaute Klasse Random, die Zufallszahlen generiert. Unsere Klasse hat also vier Felder: das Feld Zufallszahl mit einer Referenz auf ein Random-Objekt und drei string-Arrays für Fleisch, Soßen und Brot.

1

class MenüMacher { public Random Zufallszahl;

string[] Fleisch = { "Roastbeef", "Salami", "Pute", "Speck", "Braten" };

string[] Soßen = { "Süßer Senf", "Scharfer Senf", "Ketchup", "Mayo", "Aioli", "Remoulade" };

string[] Brot = { "Graubrot", "Weißbrot", "Toast", "Pumpernickel", "Ciabatta", "Brötchen" }; }

MenüMacherZufallszahlFleischSoßenBrot

MenüEintragHolen()

public string MenüEintragHolen() { string zufallsFleisch = Fleisch[Zufallszahl.Next(Fleisch.Length)]; string zufallsSoße = Soßen[Zufallszahl.Next(Soßen.Length)]; string zufallsBrot = Brot[Zufallszahl.Next(Brot.Length)]; return zufallsFleisch + " mit " + zufallsSoße + " auf " + zufallsBrot; }

Schreiben Sie eine MenüEintragHolen()-Methode, die ein Zufalls-Sandwich erzeugt.Der Zweck der Klasse ist die Zusammenstellung von Sandwiches. Fügen wir also eine entsprechende Methode hinzu. Sie nutzt die Next()-Methode des Random-Objekts, um zufällig Fleisch, Soßen und Brot aus den Arrays auszuwählen. Übergeben Sie Next() einen int-Parameter, liefert die Methode eine Zufallszahl, die kleiner als dieser Parameter ist. Heißt Ihr Random-Objekt Zufallszahl, liefert Zufallszahl.Next(7) also eine Zufallszahl zwischen 0 und 6.

Woher wissen wir nun, welchen Parameter wir der Next()-Methode übergeben müssen? Na, das ist einfach – wir übergeben einfach die Length-Eigenschaft der einzelnen Arrays. Diese liefert die Anzahl der Elemente im jeweiligen Array.

2

Die Methode steckt ein beliebiges Element aus dem Array Fleisch in die Variable zufallsFleisch, indem sie der Next()-Methode des Random-Objekts Fleisch.Length übergibt. Das Array Fleisch enthält 5 Elemente. Next(5) liefert also eine Zufalls-zahl zwischen 0 und 4.

Das Feld namens Zufalls-zahl hält eine Referenz auf ein Random-Objekt. Ein Aufruf seiner Next()-Methode erzeugt Zufalls-zahlen.

Die Klasse hat drei Felder zur Speicherung der drei String-Arrays. Diese nutzt sie, um zufällige Speise-karteneinträge zu erzeugen.

Die Methode MenüEintragHolen() liefert einen String, der ein Sandwich enthält, das aus Zufallselementen der drei Arrays gebildet wird.

Denken Sie daran, beim Zugriff auf die Array-Elemente eckige Klammern zu verwenden. Der Wert von Brot[2] ist »Toast«.

Tun Sie das!

Sehen Sie, wie diese Arrays initialisiert werden? Das nennt man einen Collection-Initialisierer. Mehr dazu in Kapitel 8.

Page 31: C# von Kopf bis Fuß

Sie sind hier 4 169

Typen und Referenzen

Ich speise immer beim Strammen

Max!Wie es funktioniert …

Fleisch[Zufallszahl.Next(Fleisch.Length)]

Fleisch ist ein Array mit Strings. Es enthält fünf Elemente mit den Indizes 0 bis 4. Fleisch[0] entspricht also »Roastbeef«, Fleisch[3] entspricht »Speck«.

Die Methode Zufallszahl.Next(7) liefert eine Zufallszahl kleiner 7. Fleisch.Length gibt die Anzahl von Elementen in Fleisch zurück. Zufallszahl.Next(Fleisch.Length) liefert Ihnen also eine Zufallszahl, die größer gleich null, aber kleiner als die Anzahl von Elementen im Array Fleisch ist.

Erstellen Sie Ihr Formular.Fügen Sie dem Formular sechs Labels, label1 bis label6, hinzu. Ergänzen Sie dann Code, der die Text-Eigenschaft jedes Labels über ein MenüMacher-Objekt setzt. Sie müssen das Objekt mit einer neuen Instanz der Klasse Random initialisieren. Hier ist der Code:

public Form1() {

InitializeComponent();

MenüMacher menü = new MenüMacher() { Zufallszahl = new Random() };

label1.Text = menü.MenüEintragHolen();

label2.Text = menü.MenüEintragHolen();

label3.Text = menü.MenüEintragHolen();

label4.Text = menü.MenüEintragHolen();

label5.Text = menü.MenüEintragHolen();

label6.Text = menü.MenüEintragHolen();

}

3

Initialisieren Sie das Zufallszahl-Feld des MenüMacher-Objekts über einen Objektinitialisierer mit einer neuen Instanz der Klasse Random.

Jetzt ist alles eingerich-tet, um über die Methode Menü EintragHolen() sechs verschiedene zufällige Sandwiches abzurufen.

Bei der Ausführung des Programms zeigen die sechs Labels sechs verschiedene zufällig zusammengestellte Sandwiches.

Eine kleine Denkaufgabe: Was geschieht, wenn Sie vergessen, das Feld Zufallszahl zu initialisieren? Wie könnte man das verhindern?

Page 32: C# von Kopf bis Fuß

170 Kapitel 4

Objekte nutzen Referenzen, um zu kommunizierenBislang haben Sie gesehen, wie Formulare mit Objekten reden, indem Sie auf ihnen Methoden aufrufen oder ihre Eigenschaften abfragen. Über Referenzen können Objekte aber auch unter-einander Methoden aufrufen. Eigentlich kann ein Formular nichts tun, was Objekte nicht auch tun können, weil ein Formular nur ein anderes Objekt ist. Wenn Objekte miteinander reden, ist das Schlüsselwort this nützlich. Jedes Mal, wenn ein Objekt das Schlüsselwort this verwendet, verweist es auf sich selbst – es ist eine Referenz, die auf das Objekt zeigt, die sie nutzt.

eine Methode, die einem Elefanten das Sprechen beibringt.Fügen wir der Klasse Elefant eine Methode hinzu. Ihr erster Parameter ist eine Nachricht von einem Elefanten. Der zweite Parameter ist der Elefant, der hier spricht:

public void SprichMitMir(string nachricht, Elefant werHierSpricht) {

MessageBox.Show(werHierSpricht.Name + " sagt: " + nachricht);

}

So sieht es aus, wenn die Methode aufgerufen wird:

Elefant kunibert = new Elefant() { Name = "Kunibert", Ohrengröße = 81 };

Elefant kunigunde = new Elefant() { Name = "Kunigunde", Ohrengröße = 63 };

kunibert.SprichMitMir("Hallo", kunigunde);

Wir haben Kuniberts SprichMitMir()-Methode aufgerufen und ihr zwei Parameter übergeben: »Hallo« und eine Referenz auf das Kunigunde-Objekt. Der Parameter werHierSpricht wird genutzt, um auf die Name-Eigenschaft des Elefanten zuzugreifen, der an SprichMitMir() übergeben wurde.

1

eine Methode, die eine andere Methode aufruft.Fügen wir der Klasse Elefant jetzt die Methode SprichMit() hinzu. Sie nutzt ein besonderes Schlüsselwort: this. Das ist eine Referenz, über die ein Objekt über sich selbst reden kann.

public void SprichMit(Elefant redeMit, string nachricht) {

redeMit.SprichMitMir(nachricht, this);

}

Schauen wir uns genauer an, wie das funktioniert.

kunibert.SprichMit(kunigunde, "Hallo");

Wird Kuniberts SprichMit()-Methode aufgerufen, nutzt sie den Parameter redeMit (der eine Referenz auf Kunigunde hat), um Kunigundes SprichMitMir()-Methode aufzurufen.

redeMit.SprichMitMir(nachricht, this);

kunigunde.SprichMitMir(nachricht, [Referenz auf Kunibert]);

Das ist, als wäre Kunigunde mit ("Hallo", kunibert) aufgerufen worden:

2

Kunibert nutzt redeMit (eine Referenz auf Kunigunde), um SprichMitMir() aufzurufen.

this wird durch eine Refe renz auf das Kunibert-Objekt ersetzt.

Diese Methode von Elefant ruft die SprichtMit()-Methode eines anderen Elefanten auf. Über sie können Elefanten miteinander reden.

Ihre Objekte sind Tratschtaschen

ElefantNameOhrengröße

WerBinIch()SprichMitMir()SprichMit()

Page 33: C# von Kopf bis Fuß

Sie sind hier 4 171

Typen und Referenzen

F: Noch mal, bitte – mein Formular ist ein Objekt?

A: Ja! Deswegen beginnt Ihr Code mit ei-ner Klassendeklaration. Öffnen Sie den Code für Ihr Formular und schauen Sie ihn sich an. Öffnen Sie dann Program.cs und blicken Sie in die Methode Main() – dort werden Sie »new Form1()« finden.

F: Warum sollte ich null verwenden?

A: In gewöhnlichen Programmen wird null auf mehrere Weisen verwendet, am häufigsten für Tests: if (kunibert == null)

Dieser Test liefert true, wenn die kunibert-Referenz auf null gesetzt ist. Außerdem wird das Schlüsselwort null verwendet, wenn man erzwingen will, dass sich der Garbage Collector ein Objekt schnappt. Haben Sie eine Referenz auf ein Objekt, das Sie nicht mehr benötigen, können Sie die Referenz auf null setzen, um es für die Garbage Collection zu markie-ren (es sei denn, es gibt noch eine weitere Referenz darauf).

F: Sie reden ständig von der Garbage Collection. Wer sammelt denn da den Müll ein?

A: Erinnern Sie sich daran, dass wir Ende des ersten Kapitels von der Common Language Runtime (oder CLR) gesprochen haben? Das ist die virtuelle Maschine, die alle .NET-Programme ausführt. Eine virtuelle Maschine ist ein Mechanismus, über den man laufende Programme vom Rest des Systems isoliert. Unter anderem verwalten virtuelle Maschinen den Speicher, den sie nutzen. Das bedeutet, dass sie alle Ihre Ob-jekte nachhalten, bemerken, wenn die letzte Referenz auf ein Objekt verschwindet, und den Speicher freigeben, den es eingenom-men hat.

Wohin noch kein Objekt gekommen istEs gibt ein weiteres wichtiges Schlüsselwort, das Sie im Zusammenhang mit Objekten verwenden werden. Wenn Sie eine neue Referenz erstellen und diese auf nichts setzen, hat sie dennoch einen Wert. Anfangs ist sie auf null gesetzt. Das heißt, dass sie auf nichts zeigt.

Hund-Objekt 2

Hund fido;

Hund lucky = new Hund();

fido = new Hund();

Noch gibt es nur ein Objekt. Die fido-Referenz ist auf null gesetzt. Hund-Objekt 1

lucky = null;

Hund-Objekt 2

Hund-Objekt 1

Jetzt zeigt fido auf ein Objekt und ist nicht länger null.

Setzen wir lucky auf null, zeigt nichts mehr auf das Objekt, das dann Futter für den Garbage Collector wird.

Es gibt keineDummen Fragen

Page 34: C# von Kopf bis Fuß

172 Kapitel 4

F: Mir ist immer noch nicht ganz klar, wie das mit den Referenzen funktioniert.

A: Über Referenzen nutzen Sie die Methoden und Felder eines Objekts. Erzeugen Sie eine Referenz auf ein Hund-Objekt, beispielsweise rex, können Sie diese Referenz nutzen, um auf Methoden zuzugreifen, die Sie für das Hund-Objekt erstellt haben. Haben Sie (nicht statische) Methoden namens Hund.Bellen() oder Hund.Betteln(), können Sie auf diese über die Referenz rex zugreifen, indem Sie rex.Bellen() oder rex.Betteln() verwenden. Sie könnten über die Referenz auch Informationen in den Feldern des Objekts ändern. Über rex.Rasse könnten Sie beispielsweise das Feld Rasse ändern.

F: Moment. Heißt das nicht, dass ich, wenn ich einen Wert über eine Referenz ändere, diesen auch für alle anderen Referenzen auf dieses Objekt ändere?

A: Ja. Ist hasso eine Referenz auf das gleiche Objekt wie rex, würde eine

Änderung von hasso.Rasse in »Beagle« auch rex.Rasse in »Beagle« ändern.

F:Das mit den Typen, die Werte unter-schiedlicher Größen aufnehmen, verstehe ich noch nicht. Was bringt das?

A: Gut. Die Sache ist, dass Variablen Ihren Zahlen eine Größe zuweisen, die von der tatsächlichen Zahl unabhängig ist. Geben Sie einer Variablen den Typ long, reserviert die CLR, auch wenn die Zahl sehr klein ist (5 beispielsweise), so viel Speicher, wie für den größten möglichen long-Wert erforderlich ist. Und das ist vernünftig, weil sich Variablen ändern und andere Werte annehmen können. Die CLR geht davon aus, dass Sie wissen, was Sie tun, und einer Variablen keinen Typ geben, den sie nicht braucht. Selbst wenn die Zahl jetzt nicht groß ist, kann sie das nach einigen Berechnungen werden, und die CLR reserviert hinreichend Speicher, um damit klarzukommen.

F: Sagen Sie mir noch einmal, was dieses »this« macht?

A: this ist eine spezielle Variable, die Sie nur in einem Objekt verwenden können. In einer Klasse nutzen Sie this, um auf ein Feld oder eine Methode dieser speziellen Instanz zu verweisen. Das ist besonders nütz-lich, wenn Sie mit einer Klasse arbeiten, deren Methoden Methoden anderer Klassen aufru-fen. Ein Objekt kann es nutzen, um eine Re-ferenz auf sich selbst an ein anderes Objekt zu senden. Ruft rex eine von hassos Methoden mit this als Parameter auf, gibt er hasso eine Referenz auf rex.

Dies und das

Code in einer Klasse, von der Objekte instantiiert werden sollen, kann das Schlüsselwort this als Referenz auf die aktuelle Instanz verwenden.

¢ Bei der Deklaration einer Variablen wird IMMER ein Typ angegeben. Die Deklaration kann mit der Initialisierung kombiniert werden.

¢ Es gibt Werttypen für Zahlen, die Zahlen unterschied-licher Größe aufnehmen. Der größte Ganzzahltyp ist long und der kleinste byte (bis 255).

¢ Werttypen haben eine Größe. Werte größerer Typen können nicht in Variablen kleinerer Typen gesteckt wer-den, egal wie groß die Daten tatsächlich sind.

¢ Bei literalen Werten gibt das Suffix F einen float (15.6F) und das Suffix M einen decimal (36.12M) an.

¢ Einige Typen kann der Compiler automatisch umwandeln (z. B. short in int). Verhindert der Compiler, dass Sie eine Variable auf einen Wert eines anderen Typs setzen, müssen Sie einen Cast (eine Umwandlung) verwenden.

¢ Die Sprache reserviert einige Wörter, die Sie nicht für die Namen von Variablen nehmen dürfen. Das sind Wörter wie for, while, using, new und andere, die in der Sprache eine besondere Bedeutung haben.

¢ Referenzen sind wie Etiketten. An ein Objekt können Sie so viele heften, wie Sie möchten.

¢ Gibt es auf ein Objekt keine Referenz mehr, kommt der Garbage Collector.

Es gibt keineDummen Fragen

Punkt für Punkt Es gibt einen speziellen Fall, in dem Sie keinen Typ deklarieren müssen – mehr dazu erfahren Sie bei der Behandlung des Schlüsselworts »var« in Kapitel 14.

Page 35: C# von Kopf bis Fuß

Sie sind hier 4 173

Typen und Referenzen

Hier sind ein Array mit Elefant-Objekten und eine Schleife, die alle durchläuft und den mit den größten Ohren sucht. Geben Sie den Wert von größteOhren.Ohrengröße nach jedem Durchlauf der for-Schleife an.

Durchlauf 1 größteOhren.Ohrengröße = _________

Durchlauf 2 größteOhren.Ohrengröße = _________

Durchlauf 3 größteOhren.Ohrengröße = _________

Durchlauf 4 größteOhren.Ohrengröße = _________

Durchlauf 5 größteOhren.Ohrengröße = _________

Durchlauf 6 größteOhren.Ohrengröße = _________

private void button1_Click(object sender, EventArgs e)

{

Elefant[] elefanten = new Elefant[7];

elefanten[0] = new Elefant() { Name = "Kunibert", Ohrengröße = 81 };

elefanten[1] = new Elefant() { Name = "Kunigunde", Ohrengröße = 63 };

elefanten[2] = new Elefant() { Name = "Lukas", Ohrengröße = 84 };

elefanten[3] = new Elefant() { Name = "Lucille", Ohrengröße = 61 };

elefanten[4] = new Elefant() { Name = "Lothar", Ohrengröße = 88 };

elefanten[5] = new Elefant() { Name = "Linda", Ohrengröße = 67 };

elefanten[6] = new Elefant() { Name = "Hubert", Ohrengröße = 89 };

Elefant größteOhren = elefanten[0];

for (int i = 1; i < elefanten.Length; i++)

{

if (elefanten[i].Ohrengröße > größteOhren.Ohrengröße)

{

größteOhren = elefanten[i]; }

}

MessageBox.Show(größteOhren.Ohrengröße.ToString());

}

Vorsicht – diese Schleife beginnt mit dem zweiten Element des Arrays (bei Index 1) und läuft sechs Mal, bis i gleich der Länge des Arrays ist.

Wir erzeugen ein Array mit 7 Elefant-Referenzen.

Jedes Array beginnt mit dem Index 0. Der erste Elefant im Array ist also elefanten[0].

Diese Zeile lässt die Referenz größteOhren auf den Elefanten zeigen, auf den elefanten[i] zeigt.

Lösung auf Seite 184.

Spitzen Sie Ihren Bleistift

Page 36: C# von Kopf bis Fuß

174 Kapitel 4

refNum = index[y];

int y = 0;

String ergebnis = "";

MessageBox.Show(ergebnis);

y = y + 1;

index[0] = 1;

index[1] = 3;

index[2] = 0;

index[3] = 2;

ergebnis += inseln[refNum];

int[] index = new int[4];

ergebnis += "\nInsel = ";

String[] inseln = new String[4];

int refNum;

while (y < 4) {

private void button1_Click (object sender, EventArgs e){

refNum = index[y];

int refNum;

inseln[0] = "Bermuda";inseln[1] = "Fidschi";inseln[2] = "Azoren";inseln[3] = "Kuba";

}

Code­MagnetenDer Code für einen Button ist völlig durcheinandergeraten. Können Sie die Schnipsel so zusammensetzen, dass sie eine funktionierende Methode ergeben, die die unten gezeigte Ausgabe erzeugt?

}

Lösung auf Seite 185.

Code-Magneten und Pool-Puzzle

Page 37: C# von Kopf bis Fuß

Sie sind hier 4 175

Typen und Referenzen

class Dreieck{ double Fläche; int Höhe; int Breite; public static void Main(String[] args) { string ergebnisse = ""; __________ ___________________________ while ( ________ ) { _________________________ _____.Höhe = (x + 1) * 2; _____.Breite = x + 4; __________________ ergebnisse += "Dreieck " + x + ", Fläche"; ergebnisse += " = " + _____.Fläche + "\n"; ___________ } ___________ x = 27; Dreieck d5 = dA[2]; dA[2].Fläche = 343; ergebnisse += "y = " + y; MessageBox.Show(ergebnisse + ", d5 Fläche = " + d5.Fläche); } void FlächeSetzen() { ____________ = (Höhe * Breite) / 2; }}

Ausgabe

Bonusfrage!

Zusatzpunkte gibt es, wenn es Ihnen gelingt, die beiden in der Ausgabe verbleibenden Lücken mit Schnipseln aus dem Pool zu füllen.

Hier ist der Einstiegspunkt für die

Anwendung. Nehmen Sie an, sie steht

in einer Datei mit den erforderlichen

»using«-Zeilen.

x = x + 1;

x = x + 2;

x = x - 1; x < 4

x < 5Dreieck [ ] dA = new Dreieck(4);

Dreieck dA = new [ ] Dreieck[4];

Dreieck [ ] dA = new Dreieck[4];dA = new Dreieck();

dA[x] = new Dreieck();

dA.x = new Dreieck();

dA[x] = FlächeSetzen();

dA.x = FlächeSetzen();

dA[x].FlächeSetzen();

int x;

int y;

int x = 0;

int x = 1;

int y = x;

Fläche

dA.Fläche

dA.x.Fläche

dA[x].FlächedA.x

dA(x)

dA[x]

x

y

28

30.0

4, d5 Fläche = 18

4, d5 Fläche = 343

27, d5 Fläche = 18

27, d5 Fläche = 343

Tipp: FlächeSetzen() ist NICHT statisch. Schauen Sie in Kapitel 3 nach, wenn Sie eine Auffrischung zum Schlüsselwort static brauchen.

Hinweis: Jeder Schnip-sel aus dem Pool kann mehrfach verwendet werden.

Pool-PuzzleSie haben die Aufgabe, die leeren

Zeilen im Code mit den Code-schnipseln aus dem Pool zu füllen. Einzelne Schnipsel können mehrfach verwendet werden, und Sie werden nicht alle Schnipsel benötigen. Das

Ziel ist es, eine Klasse zu erstel-len, die sich kompilieren lässt und

die gezeigte Ausgabe erzeugt.

Lösung auf Seite 186.

Page 38: C# von Kopf bis Fuß

176 Kapitel 4

Etwas Vergnügliches aufbauen

Erstellen Sie e in Tippspie lSie haben einen entscheidenden Punkt erreicht ... und wissen jetzt genug, um ein Spiel aufzubauen! Und so soll es funktionieren. Das Formular zeigt zufällig Buchstaben an. Tippt der Spieler auf einen dieser Buchstaben, verschwindet der Buchstabe, und die Trefferquote steigt. Tippt der Spieler auf einen falschen Buchstaben, sinkt die Treffer-quote. Mit der Zeit läuft das Spiel kontinuierlich schneller und wird mit jedem richtigen Buchstaben schwerer. Ist das ganze Formular mit Buchstaben gefüllt, endet das Spiel!

Tun Sie das!

Erstellen Sie das Formular.So soll das Formular im Formular-Designer aussehen:

1

Dazu müssen Sie Folgendes tun:

≥ Schalten Sie die Minimieren- und Maximieren-Symbole ab. Setzen Sie dann die FormBorderStyle-Eigenschaft auf Fixed3D. Das verhindert, dass der Spieler das Formular versehentlich verschiebt oder ver-ändert. Verändern Sie die Größe, sodass es erheblich breiter als hoch ist (unseres hat die Größe 876 x 174).

≥ Ziehen Sie aus dem Werkzeugkasten eine ListBox auf das Formular. Setzen Sie ihre Dock-Eigenschaft auf Fill und ihre MultiColumn-Eigenschaft auf true. Wählen Sie für Font 72 Punkte fett.

≥ Klappen Sie oben im Werkzeugkasten die Gruppe »Alle Windows Forms« auf. Jetzt haben Sie Zugriff auf eine Menge Steuerelemente. Suchen Sie nach Timer und klicken Sie doppelt darauf, um es dem Formular hinzuzufügen.

≥ Suchen Sie nach StatusStrip und klicken Sie auch darauf doppelt, um Ihrem Formular eine Statusleiste hinzuzufügen. Jetzt sollten Sie die StatusStrip- und Timer-Symbole im grauen Bereich unten im Formular-Designer sehen:

Erkennen Sie, wie man Timer einsetzen kann, um ein Formular mehrere Dinge gleichzeitig machen zu lassen? Nehmen Sie sich einen Augenblick Zeit, gehen Sie zu Punkt 3 in Anhang A und ma-chen Sie sich mit einem anderen Mittel vertraut, um das zu erreichen.

Page 39: C# von Kopf bis Fuß

Sie sind hier 4 177

Typen und Referenzen

Sie benötigen drei neue Steuer-elemente, mit denen sich aber sehr leicht arbei-ten lässt!

Obwohl Ihnen ListBox, StatusStrip und Timer noch nicht begegnet sind, wissen Sie bereits, wie man ihre Eigenschaften setzt und wie man mit ihnen im Code arbeitet. In den folgenden Kapiteln werden Sie noch einiges über sie lernen.

Richten Sie den Timer ein.Ist Ihnen aufgefallen, dass der Timer nicht in Ihrem Formular erscheint? Das liegt daran, dass er ein unsichtbares Steuerelement ist: Er hat keinen Einfluss auf das Aussehen des Formulars, sondern macht lediglich eine einzige Sache: Er ruft immer wieder eine Methode auf. Setzen Sie seine Inter-val-Eigenschaft auf 800, damit diese Methode alle 800 Millisekunden aufgerufen wird. Klicken Sie anschließend doppelt auf das timer1-Symbol im Designer. Die IDE macht dann, was sie immer tut, wenn Sie doppelt auf ein Steuerelement klicken: Sie fügt Ihrem Formular eine Methode hinzu. Diese erhält den Namen timer1_Tick, und hier ist der Code dafür:

3

private void timer1_Tick(object sender, EventArgs e){ // Der ListBox zufällig ein Zeichen hinzufügen. listBox1.Items.Add((Keys)random.Next(65, 90)); if (listBox1.Items.Count > 7) { listBox1.Items.Clear(); listBox1.Items.Add("Game Over"); timer1.Stop(); }}

Richten Sie den StatusStrip ein.Schauen Sie sich die Statusleiste in der Abbildung genau an. Auf der einen Seite befinden sich einige Labels:

2

Auf der anderen sehen Sie ein Label und eine Fortschritts-leiste:

Fügen Sie dem StatusStrip ein StatusLabel hinzu, indem Sie auf sein Menü klicken und Status-Label wählen. Tun Sie dann Folgendes:

≥ Nutzen Sie das Eigenschaften-Fenster, um (Name) auf richtigLabel und Text auf »Richtig: 0« zu setzen. Fügen Sie drei weitere StatusLabel hinzu: falschLabel, gesamtLabel und trefferquoteLabel. Setzen Sie ihre Text-Eigenschaft auf "Fehler: 0", "Gesamt: 0" bzw. "Trefferquote: 0".

≥ Fügen Sie noch ein weiteres StatusLabel ein und setzen Sie Spring auf True, TextAlign auf MiddleRight und Text auf »Schwierigkeit«. Fügen Sie abschließend eine ProgressBar hinzu, nennen Sie sie schwie-rigkeitProgressBar und setzen Sie Maximum auf 800.

≥ Setzen Sie die SizingGrip-Eigenschaft des StatusStrip auf False (drücken Sie auf Escape, wenn Sie ein StatusLabel oder eine ProgressBar ausgewählt haben, um den Fokus der IDE wieder auf den übergeordneten StatusStrip zurückzusetzen).

Sie werden gleich ein Feld namens »random« hinzufügen. Haben Sie eine Idee, welchen Typ es haben wird?

Die Klasse Timer hat eine Start()-Methode, aber diese müssen Sie für dieses Projekt nicht aufrufen. Stattdessen setzen Sie die Eigenschaft Enabled auf True, die ihn automatisch startet.

Entspannen Sie sichEntspannen Sie sich

Page 40: C# von Kopf bis Fuß

178 Kapitel 4

Der Schlüssel zu einem guten Spiel

Schreiben Sie eine Klasse, die die Spielstatistik modelliert.Wenn das Formular anzeigen soll, wie viele Tasten der Spieler insgesamt drückte, wie viele er davon richtig hatte und wie viele falsch, sowie seine Trefferquote, müssen wir alle diese Daten nachhalten. Klingt nach einer Aufgabe für eine neue Klasse! Fügen Sie Ihrem Projekt eine Klasse namens Statistik mit vier int-Feldern namens Gesamt, Falsch, Richtig und Trefferquote sowie eine Methode namens Aktualisieren hinzu, die einen bool-Parameter erwartet (true, wenn ein richtiger Buchstabe getippt wurde, und andernfalls false).

4

StatistikGesamtFalschRichtigTrefferquote

Aktualisieren()

class Statistik{ public int Gesamt = 0; public int Falsch = 0; public int Richtig = 0; public int Trefferquote = 0;

public void Aktualisieren(bool richtigeTaste) { Gesamt++;

if (!richtigeTaste) { Falsch++; } else { Richtig++; }

Trefferquote = 100 * Richtig / Gesamt; }}

Immer wenn die Methode Aktualisieren() aufgerufen wird, wird die Trefferquote neu berechnet.

Fügen Sie Ihrem Formular Felder für ein Statistik- und ein Random-Objekt hinzu und starten Sie den Timer.Sie brauchen eine Instanz der neuen Klasse Statistik, um die Daten zu speichern. Fügen Sie also ein statistik-Feld hinzu. Und Sie haben bereits gesehen, dass Sie ein Feld namens ran-dom benötigen, das ein Random-Objekt festhält.

Fügen Sie oben im Formular die beiden Felder ein:

public partial class Form1 : Form{ Random random = new Random(); Statistik statistik = new Statistik(); ...

5

Bevor Sie fortfahren, müssen Sie drei Eigenschaften setzen: die Enabled-Eigenschaft des Timers auf true, die

Maximum-Eigenschaft der ProgressBar auf 701 und die KeyPreview-Eigenschaft des Formulars auf true. Überlegen Sie einen Augenblick, warum Sie diese Eigenschaf-

ten benötigen. Was geschieht, wenn Sie sie nicht setzen?

Page 41: C# von Kopf bis Fuß

Sie sind hier 4 179

Typen und Referenzen

Die Tastenaktionen müssen verarbeitet werden.Um eine Sache müssen wir uns noch kümmern: Wenn der Spieler eine Taste drückt, muss geprüft werden, ob die Taste richtig ist (gegebenenfalls wird dann der Buchstabe aus der ListBox entfernt). Anschließend müssen die Daten im StatusStrip aktualisiert werden.

6

private void listBox1_KeyDown(object sender, KeyEventArgs e){ // Wurde eine richtige Taste gedrückt, wird das Zeichen entfernt // und das Spiel etwas schneller gemacht. if (listBox1.Items.Contains(e.KeyCode)) { listBox1.Items.Remove(e.KeyCode); listBox1.Refresh(); if (timer1.Interval > 400) timer1.Interval -= 10; if (timer1.Interval > 250) timer1.Interval -= 7; if (timer1.Interval > 100) timer1.Interval -= 2; schwierigkeitProgressBar.Value = 800 - timer1.Interval;

// Richtige Taste gedrückt, also das Statistik-Objekt aktualisieren, // indem Aktualisieren() mit dem Argument true aufgerufen wird. statistik.Aktualisieren(true); } else { // Falsche Taste gedrückt, also das Statistik-Objekt aktualisieren, // indem Aktualisieren() mit dem Argument false aufgerufen wird. statistik.Aktualisieren(false); }

// Die Labels im StatusStrip aktualisieren. richtigLabel.Text = "Richtig: " + statistik.Richtig; falschLabel.Text = "Falsch: " + statistik.Falsch; gesamtLabel.Text = "Gesamt: " + statistik.Gesamt; trefferquoteLabel.Text = "Trefferquote: " + statistik.Trefferquote + "%";}

Starten Sie Ihr Spiel.Das Spiel ist fertig! Starten Sie es und schauen Sie, wie gut Sie Ihre Arbeit gemacht haben. Eventuell müssen Sie die Schriftgröße der ListBox ändern, damit sie genau 7 Zeichen aufnimmt. Außerdem können Sie die Schwierigkeit anpassen, indem Sie die Werte ändern, die in der Methode listBox1_KeyDown() von timer1.Interval abgezogen werden.

7

Dieser Teil erhöht die Schwierigkeit, wenn der Spieler mehr Tasten richtig drückte. Sie können das Spiel vereinfachen, indem Sie die Beträge reduzieren, die von timer1.Interval abgezogen werden, oder erschweren, indem Sie sie erhöhen.

Diese if-Anweisung prüft, ob die ListBox das Zeichen enthält, das gedrückt wurde. Ist das der Fall, wird es aus der ListBox entfernt, und die Schwierigkeit des Spiels wird erhöht.

Wird eine Taste gedrückt, ruft listBox1_Key-Down() die Aktua -lisieren()-Methode des Statistik-Objekts auf, um die Statistik zu aktualisieren, und zeigt sie dann im StatusStrip an.

Aktivieren Sie im Formular-Designer die ListBox. Klicken Sie auf den Blitz im Fenster Eigenschaften. Gehen Sie zur Zeile KeyDown und klicken Sie doppelt darauf. Das weist die IDE an, eine Me-thode listBox1_KeyDown() zu erstellen, die aufgerufen wird, wenn eine Taste gedrückt wird. Hier ist der Code für die Methode:

Klicken Sie auf dieses Symbol, um die Ansicht zu ändern. Der Button links davon stellt die ursprüngliche Ansicht wieder her.

Was Sie dann sehen, sind Ereignisse. Über diese werden Sie bald einiges mehr erfahren.

Das Spiel läuft nur einmal. Wie können Sie es so ändern, dass der Spieler ein neues Spiel starten kann, wenn »Game Over« angezeigt wird?

Page 42: C# von Kopf bis Fuß

180 Kapitel 4

Lenken Sie Ihre Steuerelemente

Steuerelemente sind ganz gewöhnliche ObjekteSie haben jede Menge Formulare aufgebaut, indem Sie Steuerelemente aus der Toolbox herausgezogen haben. Doch diese Steuerelemente sind eigentlich ganz gewöhnliche Objekte. Da es Objekte sind, können Sie Referen-zen darauf erhalten und mit ihnen arbeiten wie mit den Instanzen einer Klasse, die Sie selbst geschrieben haben. Schauen wir uns ein Beispiel dafür an, indem wir ein Programm erstellen, das Label-Steuerelemente animiert und im Formular hin- und herspringen lässt.

using System.Windows.Forms;

class LabelAnimator { public Label DasLabel;

public bool Vorwärts = true;

public void Bewegen() { if (DasLabel != null) { if (Vorwärts == true) { DasLabel.Left += 5; if (DasLabel.Left >= DasLabel.Parent.Width - DasLabel.Width) { Vorwärts = false; } } else { DasLabel.Left -= 5; if (DasLabel.Left <= 0) { Vorwärts = true; } } } } }

Erstellen Sie eine neue Windows Forms- Anwendung und bauen Sie dieses Formular auf.

1

Diese Klasse hat ein Feld namens DasLabel, das den Typ Label hat, folglich also eine Referenz auf ein Label-Objekt festhält. Wie alle Referenzen ist das Feld zunächst null. Es wird auf eins der Labels auf dem Formular gesetzt.

Sie brauchen diese »using«-Zeile, weil sich Label in diesem Namensraum befindet.

Schreiben Sie eine Klasse namens LabelAnimator. Hier ist der Code dafür:

2

Ziehen Sie drei Labels und drei Buttons auf das Formu-lar. Klicken Sie doppelt auf

jeden der Buttons, um einen Event-Handler für jeden

davon einzufügen.

Ziehen Sie einen Timer auf das Formular und nutzen Sie das Eigenschaften-Fenster, um seine Enabled- Eigenschaft auf True und seine Interval-Eigen-

schaft auf 1 zu setzen. Klicken Sie dann doppelt darauf, um den timer1_Tick()-Event-Handler zu erzeugen.

Dieser Boolesche Wert wird permanent von wahr in falsch umgeschaltet, während sich das Label über das Formular bewegt.

Wenn Sie ein Label über das Formular bewegen wollen, müssen Sie eine neue Instanz der Klasse LabelAni-mator erstellen, ihr DasLabel-Feld auf ein Label-Steuerelement auf dem Formular setzen und dann immer wieder seine Bewegen()-Methode aufrufen.

Bei jedem Aufruf von Bewegen() verschiebt Label-Animator das Label, indem die Eigenschaft Left ge-ändert wird. Wenn das Feld Vorwärts wahr ist, wird das Label nach rechts bewegt, indem der Eigenschaft 5 hinzugefügt wird, andernfalls wird es nach links bewegt, indem von ihr 5 abgezogen wird.

Jedes Steuerelement hat eine Parent-Eigenschaft, die eine Referenz auf das Formular enthält, weil auch das Formular ein Objekt ist!

Tun Sie das!

Die Methode Bewegen() ermittelt, ob das Label den rechten Rand des Formulars erreicht hat, indem sie mit >= prüft, ob die Eigenschaft Left größer oder gleich der Breite des Formulars ist.

Wenn Sie ein Steuerelement über ein Formular ziehen, setzt die IDE die Eigenschaften Top und Left. Ihre Programme können diese Eigenschaften nutzen, um Steuerelemente über das Formular zu bewegen.

Warum ziehen wir die Breite des Labels von der Breite des Formulars ab?

Page 43: C# von Kopf bis Fuß

Sie sind hier 4 181

Typen und Referenzen

public partial class Form1 : Form {

public Form1() { InitializeComponent(); }

LabelAnimator[] animatoren = new LabelAnimator[3];

private void AnimationAnAus(int index, Label animator) { if (animatoren[index] == null) { animatoren[index] = new LabelAnimator(); animatoren[index].DasLabel = animator; } else { animatoren[index] = null; } }

private void button1_Click(object sender, EventArgs e) { AnimationAnAus(0, label1); }

private void button2_Click(object sender, EventArgs e) { AnimationAnAus(1, label2); }

private void button3_Click(object sender, EventArgs e) { AnimationAnAus(2, label3); }

private void timer1_Tick(object sender, EventArgs e) { for (int i = 0; i < 3; i++) { if (animatoren[i] != null) { animatoren[i].Bewegen(); } } }}

Hier ist der Code des Formulars. Schauen Sie, ob Sie herausfinden können, was hier ge-schieht. Er nutzt ein Array mit LabelAnimator-Objekten, um die Label hin- und herzubewe-gen, und der Timer-Tick-Event-Handler ruft immer wieder ihre Bewegen()-Methode auf.

3

Klicken Sie auf button1, um label1 in Bewegung zu versetzen. Klicken Sie erneut darauf, um die Bewegung zu beenden. Die beiden anderen Buttons steuern die beiden anderen Labels.

Die Labels bewegen sich zwischen den Rändern des Formulars hin und her – auch dann, wenn Sie es schmaler oder breiter machen.

Da Steuerele­mente Objekte sind, können Sie Referenzen da­rauf als Metho­denparameter übergeben und in Arrays, Fel­dern und Vari­ablen speichern.

Alle Buttons rufen die Methode Anima-tionAnAus() auf und übergeben einen Index eines Arrays und eine Referenz auf eins der Labels auf dem Formular.

Das Formular speichert ein Array mit LabelAnimator-Referenzen in einem Feld namens animatoren. Wenn die AnimationAnAus()-Methode aufgerufen wird, nutzt sie den Parameter index, um ein Element des Arrays zu prüfen. Ist das Element null, erzeugt sie ein neues LabelAnimator-Ob-jekt und speichert seine Referenz im Array. Andernfalls wird das Element gelöscht, indem die Referenz auf null gesetzt wird.

Der Timer nutzt eine for-Schleife, um die Bewegen()-Methode der LabelAnimator-Objekte aufzurufen, wenn die Referenz nicht null ist. Wird das Element auf null gesetzt, wird die Bewegung auf dem Formular abgestellt.

Verstehen Sie, was mit den Button-Event-Hand-lern passiert? Sie müssen herausfinden, wie Sie die Bewegung der Labels an- und abschalten.

Page 44: C# von Kopf bis Fuß

182 Kapitel 4

In C# gibt es ungefähr 77 reservierte Wörter, die als Schlüsselwörter bezeichnet werden. Diese Wörter werden durch den C#-Compiler reserviert und dürfen nicht als Variablennamen verwendet werden. Wenn Sie mit dem Buch durch sind, werden Sie sie alle gut kennen. Hier sind einige, die Sie schon benutzt haben. Schreiben Sie, was Ihrer Meinung nach diese Wörter in C# tun.

namespace

for

class

public

else

new

using

if

while

Namensräume sichern, dass die Namen, die Sie in Ihren Programmen verwenden, nicht mit denen im .NET Framework oder anderen in Ihrem Programm verwendeten externen Klassen kollidieren. Alle Klassen und Methoden in einem Programm sind in einem Namensraum.

Eine Schleife auf Basis von drei Ausdrücken. Im ersten wird die verwendete Variable deklariert, die dann im zweiten anhand einer Testbedingung geprüft wird. Die dritte Anweisung macht etwas mit dem Wert.

In einer Klasse definieren Sie ein Objekt. Klassen haben Eigenschaften und Methoden. Eigenschaften sind das, was eine Klasse weiß, Methoden sind das, was eine Klasse tut.

Eine öffentliche Klasse kann von jeder anderen Klasse im Projekt verwendet werden. Öffentliche Methoden und Eigenschaften können außerhalb der Klasse genutzt werden, in der sie deklariert werden.

Code, der mit else beginnt, wird ausgeführt, wenn die dazugehörige if-Anweisung fehlschlägt.

Wird verwendet, um eine neue Instanz einer Klasse zu erstellen.

Damit werden alle Namensräume aufgeführt, die in einem Programm verwendet werden. using ermöglicht Ihnen, Code aus dem .NET Framework und anderen vordefinierten Klassen anderer Hersteller sowie aus Klassen zu verwenden, die Sie selbst geschrieben haben.

Eine Möglichkeit, in einem Programm eine Bedingungsanweisung einzurichten. Sie sagt, dass eine Gruppe von Anweisungen ausgeführt werden soll, wenn eine Testbedingung wahr ist, und eine andere, wenn sie nicht wahr ist.

while-Schleifen sind Schleifen, die ausgeführt werden, solange ihre Testbedingung wahr ist.

lösung zur Übung

Lösungen zu den Übungen

Page 45: C# von Kopf bis Fuß

Sie sind hier 4 183

Typen und Referenzen

Typ-Kreuzworträtsel, Lösung

R1

U2

D3

O4

U B L E M

B S W

N5

V6

J E A

A E7

S C A P E R N

M R C V D

W8

E R9

T I T10

Y P I E U11

E C12

H A R E L N

F A B T13

O S T R I N G S

E S L T I

V14

E R K E T T E N Z15

U W E I S U N G

E I N

N N E

Z G16

A R B A G E D

Waagerecht3. Der Typ, der die größten Zahlen speichern kann. [DOUBLE] 7. \n und \r sind _____-Sequenzen. [ESCAPE] 8. Wird das für eine Variable nicht gesetzt, lässt sich Ihr Programm nicht kompilieren. [WERT] 10. Die Deklaration einer Variablen beginnt immer damit. [TYP] 12. Der Typ, der nur ein einzelnes Zeichen speichert. [CHAR] 13. Jedes Objekt hat diese Methode, die es in einen String umwandelt. [TOSTRING] 14. Das tun Sie, wenn Sie mit dem +-Operator zwei Strings zusammenkleben. [VERKETTEN] 15. Die Deklaration und die ____________ einer Variablen können zu einer Anweisung zusammengefasst werden. [ZUWEISUNG]

Senkrecht1. »namespace«, »for«, »while«, »using« und »new« sind alles Beispiele für _____________ Wörter. [RESERVIERTE] 2. Das machen die arithmetischen Operatoren für Sie mit Typen automatisch. [UMWANDELN] 4. Einer Variablen dieses Typs können Sie jeden Wert zuweisen. [OBJECT] 5. Der zweite Teil einer Variablendeklaration. [NAME] 6. Was Ihr Programm nutzt, um mit Daten zu arbeiten, die im Speicher sind. [VARIABLE] 9. Eine Variable, die auf ein Objekt zeigt. [REFERENZ] 11. Die vier Typen für ganze Zahlen, die nur positive Zahlen speichern. [UNSIGNED] 12. Das, was (int) in folgender Codezeile macht: x = (int) y; [CASTING]

Page 46: C# von Kopf bis Fuß

184 Kapitel 4

private void button1_Click(object sender, EventArgs e)

{

Elefant[] elefanten = new Elefant[7];

elefanten[0] = new Elefant() { Name = "Kunibert", Ohrengröße = 81 };

elefanten[1] = new Elefant() { Name = "Kunigunde", Ohrengröße = 63 };

elefanten[2] = new Elefant() { Name = "Lukas", Ohrengröße = 84 };

elefanten[3] = new Elefant() { Name = "Lucille", Ohrengröße = 61 };

elefanten[4] = new Elefant() { Name = "Lothar", Ohrengröße = 88 };

elefanten[5] = new Elefant() { Name = "Linda", Ohrengröße = 67 };

elefanten[6] = new Elefant() { Name = "Hubert", Ohrengröße = 89 };

Elefant größteOhren = elefanten[0];

for (int i = 1; i < elefanten.Length; i++)

{

if (elefanten[i].Ohrengröße > größteOhren.Ohrengröße)

{

größteOhren = elefanten[i]; }

}

MessageBox.Show(größteOhren.Ohrengröße.ToString());

}

Durchlauf 1 größteOhren.Ohrengröße = _________

Durchlauf 2 größteOhren.Ohrengröße = _________

Durchlauf 3 größteOhren.Ohrengröße = _________

Durchlauf 4 größteOhren.Ohrengröße = _________

Durchlauf 5 größteOhren.Ohrengröße = _________

Durchlauf 6 größteOhren.Ohrengröße = _________

Lösungen zu den Übungen

81

84

84

88

88

89

Haben Sie daran gedacht, dass die Schleife mit dem zweiten Element des Arrays be-ginnt? Warum ist das wohl so?

größteOhren hält das Element fest, das bei den bisherigen Schleifendurchläufen als das mit den größten Ohren erkannt wurde.

Die for-Schleife beginnt mit dem zweiten Elefanten und vergleicht ihn mit dem Elefanten, auf den größteOhren zeigt. Sind seine Ohren größer, lässt sie größteOhren stattdessen auf diesen Elefanten zeigen. Dann geht sie zum nächsten weiter und wieder zum nächsten … am Ende der Schleife zeigt größteOhren auf den Elefanten mit den größten Ohren.

Hier sind ein Array mit Elefant-Objekten und eine Schleife, die alle durchläuft und den mit den größten Ohren sucht. Sie sollten den Wert von größteOhren.Ohrengröße nach jedem Durch-lauf der for-Schleife angeben.

Prüfen Sie das mit dem Debugger! Setzen Sie hier den Unterbrechungspunkt und über-wachen Sie größteOhren.Ohrengröße.

LösungSpitzen Sie Ihren Bleistift

Page 47: C# von Kopf bis Fuß

Sie sind hier 4 185

Code­Magneten, LösungDer Code für einen Button ist völlig durcheinandergeraten. Konnten Sie die Schnipsel so zusammensetzen, dass sie eine funktio nierende Methode ergeben, die die unten gezeigte Ausgabe erzeugt?

private void button1_Click (object sender, EventArgs e){

String ergebnis = "";

int[] index = new int[4];int[] index = new int[4];

index[0] = 1;

index[1] = 3;

index[2] = 0;

index[3] = 2;

inseln[0] = "Bermuda";inseln[1] = "Fidschi";inseln[2] = "Azoren";inseln[3] = "Kuba";

index[3] = 2;

inseln[0] = "Bermuda";

String[] inseln = new String[4];

inseln[3] = "Kuba";

int y = 0;

int refNum;

while (y < 4) {

refNum = index[y];refNum = index[y];

ergebnis += "\nInsel = ";ergebnis += "\nInsel = ";

ergebnis += inseln[refNum];

y = y + 1;

}

}

MessageBox.Show(ergebnis);

Der String ergebnis wird aufgebaut, indem die Zeilen mit dem +=-Operator

aneinandergehängt werden.

Diese while-Schleife zieht einen Wert aus dem Array index[] und nutzt ihn als Index im Array inseln[].

Hier wird das Array inseln[] initialisiert.

Hier wird das Array index[] initialisiert.

Typen und Referenzen

Page 48: C# von Kopf bis Fuß

186 Kapitel 4

284, d5 Fläche = 343

Nach dieser Zeile haben wir ein Array mit vier Dreieck-Referenzen – aber es enthält noch keine Objekte.

Sehen Sie, dass diese Klasse den Einstiegspunkt enthält, gleich-zeitig aber eine Instanz von sich selbst erzeugt? Das ist in C# vollkommen legal.

Die Methode FlächeSetzen() nutzt die Felder Höhe und Breite, um das Feld Fläche zu setzen. Da es keine statische Methode ist, kann sie nur auf einer Instanz von Dreieck aufgerufen werden.

Die while-Schleife erzeugt vier Instanzen von Dreieck, indem sie vier Mal new verwendet.

Pool-Puzzle, Lösung

Bonusantwort

class Dreieck{ double Fläche; int Höhe; int Breite; public static void Main(String[] args) { string ergebnisse = ""; __________ ___________________________ while ( ________ ) { _________________________ _____.Höhe = (x + 1) * 2; _____.Breite = x + 4; __________________ ergebnisse += "Dreieck " + x + ", Fläche"; ergebnisse += " = " + _____.Fläche + "\n"; ___________ } ___________ x = 27; Dreieck d5 = dA[2]; dA[2].Fläche = 343; ergebnisse += "y = " + y; MessageBox.Show(ergebnisse + ", d5 Fläche = " + d5.Fläche); } void FlächeSetzen() { ____________ = (Höhe * Breite) / 2; }}

int x = 0;Dreieck[] dA = new Dreieck[4];

x < 4

dA[x] = new Dreieck();dA[x]

dA[x].FlächeSetzen();

dA[x]x = x + 1;

int y = x;

Fläche

dA[x]

Lösungen zu den Übungen

Page 49: C# von Kopf bis Fuß

Weiterlesen in …

Andrew Stellman, Jennifer Greene 3. Auflage, März 2013, 944 Seiten

ISBN 978-3-95561-596-3

http://www.oreilly.de/catalog/hfcsharp3ger/

Print-Ausgabe: 49,90 €