Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 13 Anweisungen und Ausführungsverlauf
  gp 13.1 Auswahlanweisungen
  gp 13.2 Wiederholungsanweisungen
  gp 13.3 Sprunganweisungen
  gp 13.4 Feste Zuordnung

Kapitel 13 Anweisungen und Ausführungsverlauf

In den folgenden Abschnitten werden die in C# verfügbaren Anweisungen näher beleuchtet.


Galileo Computing

13.1 Auswahlanweisungen  downtop

Die Auswahlanweisungen werden zur Durchführung von Operationen verwendet, die auf dem Wert eines Ausdrucks basieren.


Galileo Computing

13.1.1 If  downtop

Die if-Anweisung in C# erfordert, dass die Bedingung innerhalb der if-Anweisung für einen Ausdruck vom Typ bool ausgewertet wird. Mit anderen Worten, der folgende Code ist nicht zulässig:

// Fehler
using System;
class Test
{
    public static void Main()
    {
        int    value;

        if (value)        // unzulässig
            System.Console.WriteLine("true");

        if (value == 0)    // dies verwenden
            System.Console.WriteLine("true");
    }
}

Galileo Computing

13.1.2 Switch  downtop

Switch-Anweisungen sind häufig fehleranfällig. Es passiert eben zu leicht, dass versehentlich eine break-Anweisung am Ende eines case vergessen oder beim Lesen des Codes ein Fehler übersehen wird.

C# beseitigt diese Fehlerquellen, indem vorgeschrieben wird, dass am Ende jedes case-Blocks ein break oder ein goto auf einen anderen case-Block in der switch-Anweisung vorhanden sein muss.

using System;
class Test
{
    public void Process(int i)
    {
        switch (i)
        {
            case 1:
            case 2:
                // Code handhabt sowohl 1 als auch 2
                Console.WriteLine("Low Number");
                break;

            case 3:
                Console.WriteLine("3");
                goto case 4;

            case 4:
                Console.WriteLine("Middle Number");
                break;

            default:
                Console.WriteLine("Default Number");
        }
    }
}

C# ermöglicht des Weiteren die Verwendung der switch-Anweisung mit Zeichenfolgenvariablen:

using System;
class Test
{
    public void Process(string htmlTag)
    {
        switch (htmlTag)
        {
            case "P":
                Console.WriteLine("Paragraph start");
                break;
            case "DIV":
                Console.WriteLine("Division");
                break;
            case "FORM":
                Console.WriteLine("Form Tag");
                break;
            default:
                Console.WriteLine("Unrecognized tag");
                break;
        }
    }
}

Es ist nicht nur einfacher, statt mehrerer if-Anweisungen eine switch-Anweisung zu schreiben, es ist zudem auch effektiver, denn der Compiler verwendet für den Vergleich einen effizienten Algorithmus.

Bei nur wenigen Einträgen in der switch-Anweisung nutzt der Compiler eine Funktion der .NET-Laufzeitumgebung, die als Interning von Zeichenfolgen bezeichnet wird. Die Laufzeitumgebung unterhält eine interne Tabelle aller konstanten Zeichenfolgen, damit bei jeder Verwendung dieser Zeichenfolge in einem einzelnen Programm dasselbe Objekt vorliegt. In der switch-Anweisung sucht der Compiler in der Laufzeittabelle nach der switch-Zeichenfolge. Ist diese nicht vorhanden, kann es sich bei der Zeichenfolge nicht um eine der case-Zeichenfolgen handeln, sodass die default-Zeichenfolge aufgerufen wird. Ist die Zeichenfolge vorhanden, wird in den internen case-Zeichenfolgen eine sequenzielle Suche nach einer Übereinstimmung durchgeführt.

Bei vielen Einträgen im case-Block generiert der Compiler eine Hashfunktion sowie eine Hashtabelle und verwendet diese zur effizienten Suche nach der Zeichenfolge.


Galileo Computing

13.2 Wiederholungsanweisungen  downtop

Wiederholungs- oder Iterationsanweisungen werden häufig auch als Schleifenanweisungen bezeichnet und dazu eingesetzt, Operationen durchzuführen, während eine bestimmte Bedingung erfüllt ist.


Galileo Computing

13.2.1 While  downtop

Die while-Schleife funktioniert wie erwartet: Solange die Bedingung zutrifft, wird die Schleife ausgeführt. Wie die if-Anweisung, erfordert while eine boolesche Bedingung:

using System;
class Test
{
    public static void Main()
    {
        int n = 0;
        while (n < 10)
        {
            Console.WriteLine("Number is {0}", n);
            n++;
        }
    }
}

Die break-Anweisung kann dazu verwendet werden, die while-Schleife zu verlassen. Die state-Anweisung kann eingesetzt werden, um die schließende Klammer des while-Blocks für die Iteration zu überspringen und mit dem nächsten Durchlauf fortzufahren.

using System;
class Test
{
    public static void Main()
    {
        int n = 0;
        while (n < 10)
        {
            if (n == 3)
            {
                n++;
                continue;
            }
            if (n == 8)
                break;
            Console.WriteLine("Number is {0}", n);
            n++;
        }
    }
}

Dieser Code erzeugt die folgende Ausgabe:

0
1
2
4
5
6
7

Galileo Computing

13.2.2 Do  downtop

Eine do-Schleife funktioniert wie eine while-Schleife, abgesehen davon, dass die Bedingung am Ende der Schleife und nicht zu deren Beginn ausgewertet wird:

using System;
class Test
{
    public static void Main()
    {
        int n = 0;
        do
        {
            Console.WriteLine("Number is {0}", n);
            n++;
        } while (n < 10);
    }
}

Wie bei der while-Schleife, können die Anweisungen break und continue zur Steuerung des Schleifenablaufs eingesetzt werden.


Galileo Computing

13.2.3 For  downtop

Eine for-Schleife wird zum Durchlaufen verschiedener Werte eingesetzt. Die Schleifenvariable kann als Teil der for-Anweisung deklariert werden:

using System;
class Test
{
    public static void Main()
    {
        for (int n = 0; n < 10; n++)
            Console.WriteLine("Number is {0}", n);
    }
}

Der Bereich der Schleifenvariable in einer for-Schleife umfasst den Bereich der Anweisung oder des Anweisungsblocks, die auf for folgen. Der Zugriff kann nicht von außerhalb der Schleifenstruktur erfolgen:

// Fehler
using System;
class Test
{
    public static void Main()
    {
        for (int n = 0; n < 10; n++)
        {
            if (n == 8)
                break;
            Console.WriteLine("Number is {0}", n);
        }
            // Fehler; n liegt außerhalb des Bereichs
        Console.WriteLine("Last Number is {0}", n);
    }
}

Wie bei der while-Schleife, können die Anweisungen break und continue zur Steuerung des Schleifenablaufs eingesetzt werden.


Galileo Computing

13.2.4 Foreach  downtop

Hierbei handelt es sich um einen sehr gebräuchlichen Schleifenausdruck:

using System;
using System.Collections;
class MyObject
{
}
class Test
{
    public static void Process(ArrayList arr)
    {
        for (int nIndex = 0; nIndex < arr.Count; nIndex++)
        {
                // Typumwandlung für ArrayList erforderlich, in 
dem
                // Objektverweise gespeichert werden
            MyObject current = (MyObject) arr[nIndex];
            Console.WriteLine("Item: {0}", current);
        }
    }
}

Dies funktioniert zwar einwandfrei, der Programmierer muss jedoch sicherstellen, dass das Array in der for-Anweisung mit demjenigen Array übereinstimmt, das in der Indexoperation verwendet wird. Wenn diese nicht übereinstimmen, kann es schwierig sein, den Bug zu finden. Des Weiteren ist das Deklarieren einer separaten Indexvariable erforderlich, die zufällig an anderer Stelle verwendet werden könnte.

Darüber hinaus ist der Eingabeaufwand recht hoch.

Einige Sprachen, beispielsweise Perl, bieten andere Konstruktionen zur Handhabung derartiger Situationen – für C# gilt dies auch. Das vorgenannte Beispiel kann folgendermaßen umgeschrieben werden:

using System;
using System.Collections;
class MyObject
{
}
class Test
{
    public static void Process(ArrayList arr)
    {
        foreach (MyObject current in arr)
        {
            Console.WriteLine("Item: {0}", current);
        }
    }
}
 
 

Diese Methode ist sehr viel einfacher und birgt weniger mögliche Fehlerquellen. Der durch die Indexoperation zurückgegebene Typ für arr wird explizit in den Typ konvertiert, der in foreach deklariert wurde. Das ist prima, denn Auflistungstypen wie ArrayList können nur Werte vom Typ object speichern.

Foreach funktioniert nicht nur für Arrayobjekte. Tatsächlich kann foreach für jedes Objekt eingesetzt werden, dass geeignete Schnittstellen implementiert. Beispielsweise können mit foreach die Schlüssel einer Hashtabelle durchlaufen werden:

using System;
using System.Collections;
class Test
{
    public static void Main()
    {
        Hashtable    hash = new Hashtable();
        hash.Add("Fred", "Flintstone");
        hash.Add("Barney", "Rubble");
        hash.Add("Mr.", "Slate");
        hash.Add("Wilma", "Flintstone");
        hash.Add("Betty", "Rubble");

        foreach (string firstName in hash.Keys)
        {
            Console.WriteLine("{0} {1}", firstName, hash[firstName]);
        }
    }
}

Sie können benutzerdefinierte Objekte implementieren und diese unter Verwendung von foreach durchlaufen. Weitere Informationen hierzu finden Sie im Abschnitt »Indizierer und foreach« in Kapitel 19, Indizierer.

Das einzige, was mit einer foreach-Schleife nicht erreicht werden kann, ist das Ändern der Inhalte des Containers. Wenn der Container eine Indizierung unterstützt, können die Inhalte auf diesem Wege geändert werden, obwohl viele Container, die die Verwendung von foreach ermöglichen, keine Indexunterstützung bieten.

Wie bei den anderen Schleifen auch, können für die foreach-Anweisung break und continue verwendet werden.


Galileo Computing

13.3 Sprunganweisungen  downtop

Sprunganweisungen werden eben hierzu verwendet – zum Springen von einer Anweisung zu einer anderen.


Galileo Computing

13.3.1 Break  downtop

Die break-Anweisung wird dazu verwendet, den aktuellen Durchlauf oder die switch-Anweisung abzubrechen und die Ausführung nach dieser Anweisung fortzusetzen.


Galileo Computing

13.3.2 Continue  downtop

Die continue-Anweisung überspringt alle späteren Zeilen in der aktuellen Iterationsanweisung und setzt die Ausführung der Iterationsanweisung fort.


Galileo Computing

13.3.3 Goto  downtop

Anhand der goto-Anweisung kann direkt zu einem Label gesprungen werden. Da die Verwendung der goto-Anweisung häufig als risikoreich eingestuft wird, verbietet C# einige der schlimmsten Missbräuche dieser Anweisung. Eine goto-Anweisung kann beispielsweise nicht dazu verwendet werden, in einen Anweisungsblock zu springen. Die Verwendung von goto wird ausschließlich in switch-Anweisungen oder zur Steuerungsübergabe aus einer verschachtelten Schleife empfohlen, obgleich sie an beliebiger Stelle eingesetzt werden können.


Galileo Computing

13.3.4 Return  downtop

Die return-Anweisung kehrt zur aufrufenden Funktion zurück und kann optional auch einen Wert zurückgeben.


Galileo Computing

13.4 Feste Zuordnung  downtop

Die Regeln für die feste Zuordnung verhindern das Einsehen von Werten nicht zugewiesener Variablen. Nehmen Sie folgendes Codebeispiel:

// Fehler
using System;
class Test
{
    public static void Main()
    {

        int n;
        Console.WriteLine("Value of n is {0}", n);
    }
}

Bei der Kompilierung dieses Codeabschnitts gibt der Compiler einen Fehler aus, da der Wert von n verwendet wird, bevor er initialisiert wurde.

Ebenso können Operationen für Klassenvariablen nicht durchgeführt werden, bevor diese initialisiert wurden.

// Fehler
using System;
class MyClass
{
    public MyClass(int value)
    {
        this.value = value;
    }
    public int Calculate()
    {
        return(value * 10);
    }
    public int    value;
}
class Test
{
    public static void Main()
    {
        MyClass mine;

        Console.WriteLine("{0}", mine.value);            // Fehler
        Console.WriteLine("{0}", mine.Calculate());        // Fehler
        mine = new MyClass(12);
        Console.WriteLine("{0}", mine.value);        // okay
    }
}

Bei der festen Zuordnung funktionieren Strukturen (struct-Element) etwas anders. Die Laufzeitumgebung sorgt stets dafür, dass diese durch Nullen ersetzt werden, der Compiler aber prüft weiterhin, ob vor einer Verwendung eine Werteinitialisierung erfolgt.

Eine Struktur wird entweder über den Aufruf einer Erstellungsroutine oder durch das Setzen aller Instanzenmitglieder vor der Verwendung initialisiert.

using System;
struct Complex
{
    public Complex(float real, float imaginary)
    {
        this.real = real;
        this.imaginary = imaginary;
    }
    public override string ToString()
    {
        return(String.Format("({0}, {0})", real, imaginary));
    }

    public float    real;
    public float    imaginary;
}

class Test
{
    public static void Main()
    {
        Complex    myNumber1;
        Complex    myNumber2;
        Complex    myNumber3;

        myNumber1 = new Complex();
        Console.WriteLine("Number 1: {0}", myNumber1);

        myNumber2 = new Complex(5.0F, 4.0F);
        Console.WriteLine("Number 2: {0}", myNumber2);

        myNumber3.real = 1.5F;
        myNumber3.imaginary = 15F;
        Console.WriteLine("Number 3: {0}", myNumber3);
    }
}

Im ersten Abschnitt wird myNumber1 durch den Aufruf von new initialisiert. Denken Sie daran, dass bei Strukturen keine Standarderstellungsroutine vorhanden ist, daher geschieht bei diesem Aufruf nichts. Der einzige Nebeneffekt besteht darin, dass die Instanz als initialisiert markiert wird.

Im zweiten Abschnitt wird myNumber2 durch einen normalen Aufruf einer Erstellungsroutine initialisiert.

Im dritten Abschnitt wird myNumber3 initialisiert, indem allen Mitgliedern der Instanz Werte zugeordnet werden. Dies kann natürlich nur geschehen, wenn die Mitglieder als öffentlich deklariert wurden.


Galileo Computing

13.4.1 Feste Zuordnungen und Arrays  toptop

Arrays weisen bei der festen Zuordnung ein etwas anderes Verhalten auf. Bei Arrays, die aus Verweis- und Wertetypen bestehen (Klassen und struct-Elemente) kann auf die Arrayelemente zugegriffen werden, auch dann, wenn diese nicht initialisiert wurden.

Angenommen, es liegt ein Complex-Array vor:

using System;
struct Complex
{
    public Complex(float real, float imaginary)
    {
        this.real = real;
        this.imaginary = imaginary;
    }
    public override string ToString()
    {
        return(String.Format("({0}, {0})", real, imaginary));
    }

    public float    real;
    public float    imaginary;
}

class Test
{
    public static void Main()
    {
        Complex[]    arr = new Complex[10];
        Console.WriteLine("Element 5: {0}", arr[5]);        // zulässig
    }
}

Aufgrund der Operationen, die für ein Array durchgeführt werden können – beispielsweise Reverse() – kann der Compiler nicht in allen Situationen eine Ablaufverfolgung der festen Zuordnungen durchführen, was zu störenden Fehlern führen kann. Deshalb versucht er es erst gar nicht.






1    Die tatsächliche Anzahl richtet sich nach den für jede Methode zu berücksichtigenden Leistungseinbußen.

2    Wenn Ihnen Hashfunktionen und -tabellen nicht vertraut sind, sehen Sie sich die Klasse System.Collections.HashTable an oder lesen Sie ein gutes Algorithmenbuch.

3    Vgl. Edsger W. Dijkstra: GO TO considered harmful: http://www.net.org/html/history/detail/1968-goto.html.

   

Einstieg in Visual C# 2005

Visual C#

Einstieg in Visual Basic .NET 2005

Visual Basic .NET 2005






Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de