Im vorhergehenden Kapitel haben wir uns mit einfachen WENN-DANN-SONST-Bedingungen auseinandergesetzt. DIe Welt ist aber in der Regel komplexer, als als ein einfaches "Wenn etwas eintrifft, dann tue A, ansonsten tue B". Im Folgenden sehen wir uns an, wie wir komplexere Situationen mit Hilfe von Verzweigungen realisieren können.
Als Beispiel soll ein kleines Quiz dienen. Dem Nutzer stellen wir eine Frage und dieser hat 4 Antwortmöglichkeiten. Antwortet er korrekt, gewinnt er, antwortet er jedoch nicht korrekt soll je nach falscher Antwort ein anderer Text ausgegeben werden.
Die Frage soll sein:
In welchem Jahr wurde die DDR an die BRD angegliedert? (Im Volksmund Wiedervereinigung genannt)
Hier nun die möglichen Antworten, und die Reaktionen des Programms:
Auswahl | Reaktion |
---|---|
1983 | Nein, das ist ein wenig zu früh! Leider falsch. |
1999 | 1999 ist Polen schon der NATO beigetreten. Leider falsch. |
1990 | Korrekt! |
1989 | Knapp dran, 1989 war der Mauerfall. Leider falsch. |
sonst | Das war keine mögliche Antwort. Leider falsch. |
Im ersten Beispiel werden wir sehen, wie wir diese Reaktionen mit if
,else if
,else
umsetzen können, im zweiten Beispiel sehen wir eine elegantere Möglichkeit mittels switch,case
.
Als Grundlage nutzen wir folgendes Gerüst:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace if_elseif
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("In welchem Jahr wurde die DDR an die BRD angegliedert?");
Console.WriteLine("1983");
Console.WriteLine("1999");
Console.WriteLine("1990");
Console.WriteLine("1989");
Console.WriteLine("Gib deine Antwort bitte jetzt ein:");
string Antwort = Console.ReadLine();
Console.ReadLine(); /* am Ende warten */
}
}
}
Um Konvertierungsprobleme zu umgehen arbeiten wir, obwohl es sich um Zahlen handelt, mit strings
. Das ist insofern kein Problem, da wir mit den Jahres zahlen ja nicht rechnen wollen.
if, else if, else
Wir müssen nun die richtige Antwort abfangen und ein "Leider falsch"
ausgeben, sollte der Nutzer nicht dich richtige Antwort ausgewählt haben:
1
2
3
4
5
6
7
8
if (Antwort == "1990") /* richtige Antwort */
{
Console.WriteLine("Korrekt!");
}
else /* falsche Antwort */
{
Console.WriteLine("Leider falsch.");
}
Im Fall, dass der Nutzer falsch liegt, müssen wir jetzt nicht nur ein "Leider falsch."
ausgeben, sondern wir müssen auch auf jede Eingabe spezifisch reagieren. Wir müssen also im else
-Block mehrere weitere Bedingungen überprüfen und entsprechend andere Texte ausgeben. Prinzipiell gibt es mehrere Möglichkeiten, wie man das umsetzen kann, hier sehen wir uns die kompakteste Variation, die man mit if
und else
umsetzen kann an.
Es ist generell möglich, dass wir hinter jedes else
eine weitere Bedingung einfügen. Diese Art von "Unterbedingung" nennt man Verzweigung.
Wir prüfen jetzt also, ob als Antwort "1993"
gewählt wurde und ansonsten prüfen wir auf die beiden anderen falschen Antworten:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
else /* falsche Antwort */
{
if (Antwort == "1983")
{
Console.WriteLine("Nein, das ist ein wenig zu früh!");
}
else if (Antwort == "1999")
{
Console.WriteLine("1999 ist Polen schon der NATO beigetreten.");
}
else if (Antwort == "1989")
{
Console.WriteLine("Knapp dran, 1989 war der Mauerfall.");
}
Console.WriteLine("Leider falsch.");
}
Wir hätten die neu hinzugekommen else
auch weglassen können, bis hier hin würde das kaum einen Unterschied machen. Das Programm würde im Fall, dass der Nutzer "1983"
als Antwort angibt, auch die anderen Bedingungen überprüfen, wodurch das Programm etwas langsamer würde. Das ist bei nicht rechenintensiven Anwendungen aber meist zu vernachlässigen.
Die else
vor den drei Bedingungen sind aber dann wichtig, wenn wir auch den Fall klären wollen, wenn der Nutzer keine unserer Möglichkeiten eingeben hat. Hier brauchen wir die else if
Anweisungen, da wir nun nach Zeile 14 einfach ein else
einfügen können und den Nutzer entsprechend darauf hinweisen können, dass er keine der möglichen Vorgaben verwendet hat. Hätten wir keine else if
verwendet, wüssten wir nicht, dass die darüber stehenden Bedingungen nicht erfüllt wurden. (Eine Möglichkeit es doch herauszufinden, lernen wir im Kapitel komplexe Ausdrücke kennen).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
else /* falsche Antwort */
{
if (Antwort == "1983")
{
Console.WriteLine("Nein, das ist ein wenig zu früh!");
}
else if (Antwort == "1999")
{
Console.WriteLine("1999 ist Polen schon der NATO beigetreten.");
}
else if (Antwort == "1989")
{
Console.WriteLine("Knapp dran, 1989 war der Mauerfall.");
}
else
{
Console.WriteLine("Das war keine mögliche Antwort.");
}
Console.WriteLine("Leider falsch.");
}
Sollte sich der Nutzer also jetzt für eine nicht vorgegebene Antwort entscheiden, dann wird ihm dies auch entsprechend zurück gemeldet.
switch, case
Eine elegantere (wir wissen ja, Programmierer sind, wie Mathematiker, ...) Möglichkeit gibt es zum else if
aber dennoch. Stellen wir uns vor, dass wir nicht nur einzelne Fälle unterscheiden wollen, sondern um die zwanzig. In dem Fall wäre es doch netter, wenn wir die ganzen if
und else
nicht schreiben müssten. Insbesondere einen Teil wiederholen wir ja immer wieder, nämlich die Überprüfung Antwort == "..."
. Kommen wir jetzt auf die Idee, dass die Variable schlecht gewählt wurde, müssten wir sie auch 20 mal ändern. Um das zu umgehen und um komplexere Verzweigungen zu erzeugen (reißen wir hier nur an), gitb es die switch
-case
-Anweisungen.
Ein switch
wählt dabei die Variable aus, die überprüft werden soll, und das case
gibt die entsprechende Möglichkeit an. Ein Anfang wäre:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
switch (Antwort)
{
case "1990":
Console.WriteLine("Korrekt!");
break;
case "1983":
Console.WriteLine("Nein, das ist ein wenig zu früh!");
break;
case "1999":
Console.WriteLine("1999 ist Polen schon der NATO beigetreten.");
break;
case "1989":
Console.WriteLine("Knapp dran, 1989 war der Mauerfall.");
break;
default:
Console.WriteLine("Das war keine mögliche Antwort.");
break;
}
Das break;
sorgt dafür, dass das Programm nach dem Ausführen eines "Falls" (case
) keine weiteren Fälle überprüft.
Wir müssten also jetzt eigentlich im Fall einer jeden falschen Antwort ein Console.WriteLine("Leider falsch.");
nach der jeweiligen Ausgabe hinzufügen. Das würde aber dem DRY(Dont-Repeat-Yourself)-Prinzip entgegenstehen, wesswegen wir ja ein switch
-case
nutzen wollten. Sollten wir nämlich einsehen, dass vielleicht jedes Mal ein anderer Text bei einer falschen Antwort ausgegeben werden soll, müssten wir das entsprechend der Anzahl der falschen Antworten oft ändern.
Hier kommt uns ein Element zu Gute, welches die Entwickler der Programmiersprache C# mit eingebaut haben, das der ein oder andere aus älteren Scriptsprachen vielleicht kennt. Es handelt sich um ein goto
, was heutzutage in der Regel von Programmieren nicht mehr gern gesehen wird, da es dafür sorgt, dass Programmcode schlecht leserlich wird. Es gibt aber hin und wieder sinnvolle Einsatzmöglichkeiten, die auch der besseren Lesbarkeit zuträglicher sind, als wenn wir ohne dem goto
auskommen müssten.
In unserem Beispiel wollen wir jetzt also nach jeder falschen Antwort nicht mehr das switch
mit einem break;
verlassen, sondern mit einem goto case "falsch";
zu einem Fall springen, den wir eigens dafür anlegen. Dieser Fall soll sich nun darum kümmern, dass eben der Text "Leider falsch."
ausgegeben wird.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
switch (Antwort)
{
case "1990":
Console.WriteLine("Korrekt!");
break;
case "1983":
Console.WriteLine("Nein, das ist ein wenig zu früh!");
goto case "falsch";
case "1999":
Console.WriteLine("1999 ist Polen schon der NATO beigetreten.");
goto case "falsch";
case "1989":
Console.WriteLine("Knapp dran, 1989 war der Mauerfall.");
goto case "falsch";
case "falsch":
Console.WriteLine("Leider falsch.");
break;
default:
Console.WriteLine("Das war keine mögliche Antwort.");
goto case "falsch";
}
Mit dem goto
können wir einen sehr weiten Verzweigungsbaum aufbauen. Dieser ist unter Umständen schlecht zu lesen. Würden wir aber mit else if
einen ähnlichen Baum aufbauen, wären wir (je nach Komplexität) nicht so flexibel wie mit einem goto
beziehungsweise die Lesbarkeit wäre in der Regel eher schlechter als in einem switch
-case
inklusive einem goto
.
In C# muss - im Gegensatz zu anderen C-Derivaten - zwangläufig jeder Fall zum Beispiel mit einem break;
oder goto;
abgeschlossen werden. Möchte man mehrere Fälle zusammen abhandeln, kann man das aber dennoch:
1
2
3
4
5
6
7
8
9
10
11
12
switch (Antwort)
{
case "1990":
Console.WriteLine("Korrekt!");
break;
case "1983":
case "1999":
case "1989":
default:
Console.WriteLine("Leider falsch.");
break;
}