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;
}

Projektdateien