Anweisungen
Bedingungen
Allen Beispielen in den vergangenen Kapiteln ist gemein, dass sie noch keine einzige
Zeile Code enthalten, der im klassischen Sinn ausgeführt werden kann. Sämtliche Konzepte,
die bislang thematisiert wurden, dienen lediglich der Modellierung und Strukturierung
von Daten und Anwendungen. Sie bilden also nur den äußeren Rahmen für eine Anwendung,
deren Inneres aber noch mit konkretem Code gefüllt und damit zum Leben erweckt werden
muss.
Zur Steuerung des Ablaufs einer Anwendung gibt es in C# zwei wesentliche Konzepte:
Bedingungen und Schleifen. Während Codeabschnitte mit Hilfe von Bedingungen nur unter
bestimmten Umständen ausgeführt werden und die Ausführung dadurch dynamisch an den
äußeren Kontext angepasst werden kann, dienen Schleifen der wiederholten Ausführung
von Code, um beispielsweise eine Menge gleichartiger Daten zu verarbeiten.
Die einfachste Anweisung zur Abfrage einer Bedingung wurde im Kapitel zu Ereignissen
bereits erwähnt, da dort überprüft werden musste, ob an den Delegaten eines Ereignisses
überhaupt Methoden angehängt worden sind.
Der Anweisung
if wird dabei in runden Klammern ein Ausdruck
übergeben, der von C# ausgewertet und entweder zu
true oder
false evaluiert wird. Sofern der Ausdruck
true
ergibt, wird der Rumpf von
if ausgeführt, andernfalls nicht.
In dem folgenden Beispiel wird also überprüft, ob der Storing-Delegat ungleich dem
Literal
null ist. Wenn dem so ist, wird der Rumpf ausgeführt
und das entsprechende Ereignis ausgelöst.
| C# |
1
2
3
4
5
6
|
// Check if there are any event handlers.
if(this.Storing != null)
{
// Raise the storing event.
this.Storing(this, null);
}
|
Die geschweiften Klammern um den Rumpf sind optional, solange der Rumpf nur aus einer
einzelnen Zeile besteht, allerdings ist es guter Stil, die geschweiften Klammern in
jedem Fall zu verwenden.
Die einfache
if-Anweisung ermöglicht zwar bereits die bedingte
Ausführung von Code, allerdings erfordert eine exklusive Ausführung zweier Codeabschnitte
zwei Abfragen, die einander außer in der Prüfung auf
true oder
false gleichen.
| C# |
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
28
29
30
31
32
33
34
35
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Check whether the condition evaluates to
// true. If so, run the specified code.
if (condition == true)
{
// TODO gr: Do something ...
// 2008-01-03
}
// Check whether the condition evaluates to
// false. If so, run the specified code.
if (condition == false)
{
// TODO gr: Do something else ...
// 2008-01-03
}
}
}
}
|
Daher gibt es das Schlüsselwort
else, das einen weiteren Rumpf
einleitet, der ausgeführt wird, wenn die bei
if genannte Bedingung
eben nicht zu
true evaluiert wird. Die erneute Angabe einer weiteren
Bedingung kann somit also entfallen.
| C# |
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
28
29
30
31
32
33
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Check whether the condition evaluates to
// true. If so, run the specified code. If not,
// run the second block.
if (condition == true)
{
// TODO gr: Do something ...
// 2008-01-03
}
else
{
// TODO gr: Do something else ...
// 2008-01-03
}
}
}
}
|
Ein wichtiger Aspekt bei der Überprüfung der Bedingung ist, dass die explizite Angabe des
Vergleichs mit
true oder
false entfallen
kann, da eine logische Bedingung automatisch zu einem der beiden Literale evaluiert wird.
Ein Vergleich auf
false kann dabei mit Hilfe des Operators !
durchgeführt werden.
| C# |
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
28
29
30
31
32
33
34
35
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Check whether the condition evaluates to
// true.
if (condition)
{
// TODO gr: Do something ...
// 2008-01-03
}
// Check whether the condition evaluates to
// false.
if (!condition)
{
// TODO gr: Do something ...
// 2008-01-03
}
}
}
}
|
Unter Umständen kann es notwendig sein, mehr als zwei Optionen zu prüfen. Dazu kann auf
mehrere
if-Anweisungen zurückgegriffen werden, die ineinander
verschachtelt werden.
| C# |
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define some conditions.
bool condition1 = 23 < 42;
bool condition2 = 17 < 23;
// Check whether the first condition evaluates
// to true.
if (condition1)
{
// TODO gr: Do something ...
// 2008-01-03
}
else
{
// Check whether the second condition
// evaluates to true.
if (condition2)
{
// TODO gr: Do something else ...
// 2008-01-03
}
else
{
// TODO gr: Do something completely
// else ...
// 2008-01-03
}
}
}
}
}
|
Zunächst wird also geprüft, ob die erste Bedingung zutrifft, wenn nein, wird in den
entsprechenden
else-Block verzweigt, in dem wiederum die zweite
Bedingung geprüft wird, und so weiter. Der innerste
else-Block
wird dabei nur ausgeführt, wenn alle vorangegangenen Bedingungen fehlgeschlagen sind.
Obwohl dieses Vorgehen zum gewünschten Ziel führt, wird die Darstellung bei einer
zunehmenden Anzahl von Ebenen unübersichtlich. Daher gibt es die Möglichkeit, weitere
Abfragen mit dem Konstrukt
else if auf der
gleichen Ebene wie das erste
if zu positionieren. Die Angabe des
abschließenden
else ohne Bedingung ist dabei wiederum optional.
| C# |
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
28
29
30
31
32
33
34
35
36
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define some conditions.
bool condition1 = 23 < 42;
bool condition2 = 17 < 23;
if (condition1)
{
// TODO gr: Do something ...
// 2008-01-03
}
else if (condition2)
{
// TODO gr: Do something else ...
// 2008-01-03
}
else
{
// TODO gr: Do something completely else ...
// 2008-01-03
}
}
}
}
|
Werden innerhalb einer Bedingung mehrere Bedingungen angegeben und mit Hilfe von logischen
Operatoren wie && oder || verknüpft, werden diese in C# von links nach rechts
ausgewertet. Zu beachten ist hierbei, dass C# die Auswertung abbricht, sobald das endgültige
Ergebnis des Gesamtausdrucks feststeht. Diese Technik wird als Kurzschlussevaluierung
bezeichnet.
Werden beispielsweise zwei Bedingungen mit Hilfe von && verknüpft und ergibt
bereits die erste Bedingung
false, so wird die zweite
Bedingung nicht mehr ausgewertet, da der Gesamtausdruck unabhängig von deren Ergebnis
in jedem Fall nur noch zu
false evaluiert werden kann.
Da es häufig Abfragen der Art gibt, dass einer Variablen entweder ein oder ein anderer
Wert zugewiesen werden soll, gibt es dafür in C# zwei abkürzende Schreibweisen. Handelt
es sich bei der entsprechenden Variablen um eine Variable des Typs
bool,
so ist es kürzer, an Stelle von
| C# |
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
28
29
30
31
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Set result to true if the condition evaluates
// to true, otherwise set the result to false.
bool result;
if (condition)
{
result = true;
}
else
{
result = false;
}
}
}
}
|
den verkürzten Ausdruck
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Set the result to true if the condition
// evaluates to true, otherwise set the result
// to false.
bool result = condition;
}
}
}
|
zu verwenden. Ebenso kann bei Variablen jedes beliebigen anderen Typs der einzige Operator
mit drei Operanden verwendet werden, der sogenannte triäre Operator. An Stelle von
| C# |
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
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Set the result to 23 if the condition
// evaluates to true, otherwise set the result
// to 42.
int result;
if (condition)
{
result = 23;
}
else
{
result = 42;
}
}
}
}
|
lässt sich unter Zuhilfenahme des triären Operators
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
bool condition = 23 < 42;
// Set the result to 23 if the condition
// evaluates to true, otherwise set the result
// to 42.
int result = condition ? 23 : 42;
}
}
}
|
schreiben. Des weiteren besteht im Zusammenhang mit nullbaren Wertetypen häufig der
Wunsch, einen Standardwert zuzuweisen, falls der nullbare Wertetyp dem Literal
null entspricht. An Stelle der umfangreichen Abfrage
| C# |
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
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a nullable type.
int? nullableType = null;
// Set the value type to 23 if the nullable type
// is null, otherwise set it to the value of the
// nullable type.
int valueType;
if (nullableType == null)
{
valueType = 23;
}
else
{
valueType = (int)nullableType;
}
}
}
}
|
kann in C# seit der Version 2.0 der Operator ?? verwendet werden, so dass statt
dessen
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a nullable type.
int? nullableType = null;
// Set the value type to the value of the
// nullable type if it is not equal to null,
// otherwise set the value type to 23.
int valueType = nullableType ?? 23;
}
}
}
|
geschrieben werden kann. Schließlich gibt es neben
if noch
die Anweisung
switch zur bedingten Ausführung von Code,
die sich insbesondere dann anbietet, wenn für jede Ausführungsalternative der
gleiche Ausdruck ausgewertet werden soll und die Ausführung nur vom jeweiligen
Ergebnis abhängt.
Die
switch-Anweisung erwartet die Bedingung ebenfalls
innerhalb von runden Klammern, die einzelnen Fälle werden aber über entsprechende
case-Zweige abgedeckt. Ebenso wie bei
if
gibt es auch bei
switch einen optionalen Ausführungspfad ohne
Bedingung, der ausgeführt wird, falls jeder vorige Option fehlschlägt, und der mit
Hilfe des Schlüsselwortes
default eingeleitet wird.
Alle Blöcke müssen bei
switch mit dem Schlüsselwort
break abgeschlossen werden.
| C# |
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
28
29
30
31
32
33
34
35
36
37
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the condition.
int condition = 23;
// Execute code depending on the condition.
switch (condition)
{
case 23:
// TODO gr: Do something ...
// 2008-01-03
break;
case 42:
// TODO gr: Do something else ...
// 2008-01-03
break;
default:
// TODO gr: Do something completely
// else ...
// 2008-01-03
break;
}
}
}
}
|
Die einzige Ausnahme davon ist das sogenannte Durchfallen von einer Alternative zu der
darauffolgenden, was genutzt werden kann, falls beide Alternativen den gleichen
Ausführungsblock verwenden sollen. Sobald der durchfallende Block allerdings eine einzige
Zeile Code enthält, wird von C# ein entsprechender Fehler bei der Übersetzung ausgelöst.
| C# |
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
28
29
30
31
32
33
34
35
36
37
38
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a condition.
int condition = 23;
// Execute code depending on the condition.
switch (condition)
{
case 17:
case 23:
// TODO gr: Do something ...
// 2008-01-03
break;
case 42:
// TODO gr: Do something else ...
// 2008-01-03
break;
default:
// TODO gr: Do something completely
// else ...
// 2008-01-03
break;
}
}
}
}
|
Prinzipiell kann in C# auch gezielt von einem Ausführungsblock in einen anderen
gesprungen werden, um beispielsweise trotz enthaltenem Code in einen weiteren
Ausführungsblock durchzufallen. Dies geschieht mittels des Schlüsselwortes
goto. Da dies in der Praxis aber als schlechter Stil
angesehen wird, wird an dieser Stelle nicht näher darauf eingegangen.
Schleifen
Während durch eine Bedingung wie
if oder
switch
definiert werden kann, welche Anweisungen unter welchen Umständen ausgeführt werden,
können Anweisungen mit Hilfe von Schleifen wiederholt ausgeführt werden, wobei die Anzahl
der Wiederholungen entweder vorher festgelegt wird oder sich dynamisch während der Ausführung
ergibt.
Die einfachste Schleife in C# ist eine reine Zählschleife, welche die in ihrem Rumpf
enthaltenen Anweisungen in einer vorherbestimmten Anzahl an Durchläufen ausführt. Diese Schleife
wird mittels des Schlüsselworts
for implementiert. Innerhalb runder
Klammern werden mit Hilfe dreier Ausdrücke der Initialisierungsausdruck, das Abbruchkriterium
und der Aktualisierungsausdruck angegeben.
Um beispielsweise eine Anweisung n Mal auszuführen, wird zu Beginn der Schleife eine
Variable mit dem Wert 0 initialisiert und anschließend in jedem Durchlauf um eins erhöht,
bis die Schleife n Mal durchlaufen wurde. Diese Variable wird auch als Schleifenvariable
oder Schleifeninvariante bezeichnet und enthält in jedem Durchlauf den Wert des jeweils
aktuellen Durchlaufs.
| C# |
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
28
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the upper limit for the loop.
int n = 23;
// Initialize the invariant with 0 and execute
// the loop as long as i is less than n.
for (int i = 0; i < n; i++)
{
// Print the square numbers to the console.
Console.WriteLine("The square number of " +
i + " is " + i * i + ".");
}
}
}
}
|
Es hat sich in der Praxis eingebürgert, die Invariante mit i zu bezeichnen, obwohl dies
den Namenskonventionen für lokale Variablen widerspricht. Sofern Schleifen verschachtelt
werden, werden für die Invarianten der inneren Schleifen fortlaufend die Buchstaben ab j
verwendet.
Des weiteren ist es guter Stil, die Invariante einer Schleife mit dem Wert 0 und nicht
mit 1 zu initialisieren, wobei abhängig vom Kontext auch vollständig andere Startwerte
sinnvoll sein können. Ebenso wird die Invariante in den meisten Fällen bei jedem Durchlauf
um eins erhöht, jedoch kann auch dies je nach Bedarf beliebig gewählt werden. Unter
Umständen sind auch Schleifen denkbar, deren Initialisierungsausdruck die Invariante
zunächst auf einen hohen Wert setzt, der dann in jedem Schleifendurchlauf verringert
wird - kurz, der Fantasie sind dabei keine Grenzen gesetzt.
Da das Abbruchkriterium vor jedem neuen Durchlauf überprüft wird, kann es vorkommen, dass
eine
for-Schleife überhaupt nicht ausgeführt wird. Dann nämlich,
wenn der Initialisierungsausdruck die Invariante auf einen Wert setzt, für den das
Abbruchkriterium zu
false evaluiert wird.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
for (int i = 0; i > 1; i++)
{
// This loop is never executed, since i is
// initialized with 0, so i > 1 evaluates
// to false.
}
}
}
}
|
Der wesentliche Nachteil der
for-Schleife ist, dass von vornherein
feststehen muss, wie viele Durchläufe ausgeführt werden sollen. Falls dies nicht bekannt
ist, sondern nur ein Abbruchkriterium feststeht, kann in C# die
while-Schleife eingesetzt werden. Ihr Prinzip entspricht dem
der
for-Schleife, wobei sich die Angabe innerhalb der runden
Klammern auf das Abbruchkriterium beschränken.
| C# |
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
28
29
30
31
32
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the upper limit.
int upperLimit = 23;
// Iterate over all square numbers from 0 to the
// upper limit.
int i = 0;
while (i < upperLimit)
{
// Print the square number to the console.
Console.WriteLine("The square number of " +
i + " is " + i * i + ".");
// Increase i by 1.
i++;
}
}
}
}
|
Falls sich in einem Durchlauf nichts an der Gültigkeit des Abbruchkriteriums ändert,
wird die Schleife ein weiteres Mal durchlaufen. Um keine Endlosschleife zu erhalten,
ist es allerdings wichtig, darauf zu achten, dass sich das Abbruchkriterium zumindest
überhaupt ändern könnte.
Wie bei der
for-Schleife wird auch die
while-Schleife unter Umständen kein einziges Mal durchlaufen,
falls nämlich das Abbruchkriterium von vornherein zu
false
evaluiert wird. Daher werden diese beiden Schleifen auch als abweisende Schleifen
bezeichnet.
Sofern eine Schleife in jedem Fall mindestens ein Mal durchlaufen werden soll, gibt
es in C# auch eine nichtabweisende Variante der
while-Schleife,
die sich des Schlüsselworts
do bedient.
| C# |
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
28
29
30
31
32
33
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the upper limit.
int upperLimit = 23;
// Iterate over all square numbers from 0 to the
// upper limit.
int i = 0;
do
{
// Print the square number to the console.
Console.WriteLine("The square number of " +
i + " is " + i * i + ".");
// Increase i by 1.
i++;
}
while (i < upperLimit);
}
}
}
|
Abgesehen davon, dass die
do-Schleife das Abbruchkriterium erst
nach und nicht vor dem Durchlauf überprüft, ist sie in ihrer sonstigen Arbeitsweise identisch
mit der
while-Schleife.
Beachtenswert bei diesen beiden Schleifen ist, dass der öffnenden Anweisung nie ein
Semikolon folgt, das schließende
while bei der
do-Schleife allerdings durch ein Semikolon abgeschlossen wird.
Sprunganweisungen
In manchen Fällen kann es je nach Kontext notwendig sein, die Ausführung einer Schleife mit sofortiger
Wirkung abzubrechen. Dies ist in C# mit Hilfe des Schlüsselworts
break
möglich, das bereits bei den einzelnen Zweigen einer
switch-Anweisung
Verwendung fand.
| C# |
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
28
29
30
31
32
33
34
35
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the upper limit.
int upperLimit = 23;
// Iterate over all square numbers from 0 to the
// upper limit.
for (int i = 0; i < upperLimit; i++)
{
// Print the square number to the console.
Console.WriteLine("The square number of " +
i + " is " + i * i + ".");
// Check whether the loop shall still be continued.
// If not, break.
if (i > 17)
{
break;
}
}
}
}
}
|
break bricht die Ausführung der aktuellen Schleife ab, indem
es diese verlässt und in die umgebende Struktur springt. Sofern dies beispielsweise bei
geschachtelten Schleifen wiederum eine Schleife ist, wird diese allerdings nach wie vor
ausgeführt, da
break nur eine einzelne Ebene verlässt.
Während
break einen Schleifenablauf vollständig abbricht, kann
es unter Umständen nur gewünscht sein, den aktuellen Durchlauf abzubrechen, prinzipiell
aber innerhalb der Schleifenausführung zu bleiben, das heißt, direkt mit dem nächsten
Durchlauf fortzufahren. Dies geschieht in C# mit Hilfe des Schlüsselwortes
continue.
| C# |
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
28
29
30
31
32
33
34
35
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Set the upper limit.
int upperLimit = 23;
// Iterate over all square numbers from 0 to the
// upper limit.
for (int i = 0; i < upperLimit; i++)
{
// Check whether the current number is odd.
// If so, skip the current iteration.
if (i % 2 != 0)
{
continue;
}
// Print the square number to the console.
Console.WriteLine("The square number of " +
i + " is " + i * i + ".");
}
}
}
}
|
foreach
Zu guter letzt gibt es in C# noch eine weitere Schleife, die allerdings über kein
entsprechendes Pendant in MSIL verfügt, sondern die lediglich als komfortable Lösung
in C# enthalten ist, vom Compiler während der Übersetzung aber in eine klassische
while-Schleife umgewandelt wird.
Diese Schleife dient dazu, alle Elemente einer Aufzählung zu durchlaufen, ohne den
Aufwand, zunächst die Länge dieser Auflistung ermitteln und eine Schleifeninvariante
erzeugen zu müssen. Implementiert wird diese Schleife mit Hilfe des Schlüsselwortes
foreach.
| C# |
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;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Iterate over all colors.
foreach (string color in colors)
{
// Print the current color to the console.
Console.WriteLine(color);
}
}
}
}
|
Intern wandelt der Compiler diese Schleife in eine
while-Schleife
um, die mit sämtlichen Aufzählungen arbeiten kann, welche die Schnittstelle IEnumerator
implementieren. Dies sind nicht nur sämtliche Arrays, sondern auch etliche der in den
Namensräumen System.Collections und System.Collections.Generic enthaltenen Typen.
Im Gegensatz zu den anderen Schleifen, bei denen die Invariante als Index für eine
Aufzählung dienen kann, liefert die Invariante der
foreach-Schleife
direkt ein Element der Aufzählung. Dabei wird allerdings nicht garantiert, in welcher
Reihenfolge diese Elemente zurückgegeben werden. Insbesondere wird also nicht garantiert,
dass zwei
foreach-Schleifen über eine gemeinsame Aufzählung die
darin enthaltenen Elemente in der identischen Reihenfolge durchlaufen.
Gelegentlich steht die Aufzählung, über die mit einer
foreach-Schleife iteriert werden soll, nicht fest, sondern
soll erst zur Laufzeit von einer Methode erzeugt werden. Seit C# 2.0 gibt es dafür das
Schlüsselwort
yield, das genau diese Funktionalität
ermöglicht.
Damit eine Methode als Aufzählung für eine
foreach-Schleife
dienen kann, muss sie über die Schnittstelle IEnumerable als Rückgabetyp verfügen.
Allerdings gibt sie nicht die gesamte Aufzählung auf einmal zurück, sondern liefert bei
jedem Methodenaufruf den nächsten Wert der Aufzählung.
Das Schlüsselwort
yield bewirkt, dass der nächste Aufruf die
Methode an der Stelle fortsetzt, an der sie im vorherigen Durchlauf verlassen wurde.
yield ermöglicht also eine zeitweise Unterbrechung der
Methodenausführung.
| C# |
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
using System;
using System.Collections;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Iterate over all square numbers.
foreach (int i in this.GetNextSquareNumber())
{
// Print the square number to the console.
Console.WriteLine(
"The next square number is " + i + ".");
}
}
/// <summary>
/// Gets the next square number.
/// </summary>
/// <returns>The next square number.</returns>
private IEnumerable GetNextSquareNumber()
{
// Initialize the square numbers with 0.
int i = 0;
// Iterate endlessly over all numbers.
while (true)
{
// Return the current square number to the
// caller.
yield return i * i;
// Increase i by 1.
i++;
}
}
}
}
|