Skip to content

3. Grundlagen der Sprache C#

3.1. Einführung

Wir beginnen damit, C# als klassische Programmiersprache zu betrachten. Auf Klassen gehen wir später ein. Ein Programm besteht aus zwei Komponenten:

  • Daten
  • die Anweisungen, die diese verarbeiten

Im Allgemeinen versuchen wir, Daten von Anweisungen zu trennen:

3.2. C#-Daten

C# verwendet die folgenden Datentypen:

  1. Ganzzahlen
  2. Reelle Zahlen
  3. Dezimalzahlen
  4. Zeichen und Zeichenfolgen
  5. Boolesche Werte
  6. Objekte

3.2.1. Vordefinierte Datentypen

C#-Typ
Typ .NET
Dargestellte Daten
Suffix
Literalwerte
Codierung
Wertebereich
Zeichen
Zeichen (S)
Zeichen
 
2 Bytes
Unicode-Zeichen (UTF-16)
Zeichenkette
Zeichenkette (C)
Zeichenkette
  
Verweis auf eine Folge von Unicode-Zeichen
int
Int32 (S)
ganze Zahl
 
4 Bytes
[-2³¹; 2³¹-1] [-2147483648; 2147483647]
uint
UInt32 (S)
..
U
4 Bytes
[0, 2³²-1] [0, 4294967295]
long
Int64 (S)
..
L
8 Bytes
[-263, 263 -1] [-9223372036854775808, 9223372036854775807]
ulong
UInt64 (S)
..
UL
8 Bytes
[0, 264 -1] [0, 18446744073709551615]
sbyte
 
..
 
1 Byte
[-27, 27 -1] [-128, +127]
Byte
Byte (S)
..
 
1 Byte
[0 , 28 -1] [0,255]
short
Int16 (S)
..
 
2 Bytes
[-215, 215-1] [-32768, 32767]
ushort
UInt16 (S)
..
 
2 Bytes
[0, 216-1] [0,65535]
Float
Single (S)
reelle Zahl
F
4 Bytes
[1,5 × 10⁻⁴⁵, 3,4 × 10³⁸ in absoluten Werten
Doppel
Double (S)
..
D
8 Byte
[-1,7 × 10³⁰⁸, 1,7 × 10³⁰⁸ in absoluten Werten
dezimal
Dezimal (S)
Dezimalzahl
M
16 Bytes
[1,0 × 10⁻²⁸, 7,9 × 10²⁸] im Absolutwert mit 28 signifikanten Stellen
bool
Boolesche (S)
..
 
1 Byte
wahr, falsch
Objekt
Objekt (C)
Objektreferenz
  
Objektreferenz

Oben sind C#-Typen durch ihren entsprechenden .NET-Typ gekennzeichnet, mit dem Vermerk (S), wenn es sich um eine Struktur handelt, und (C), wenn es sich um eine Klasse handelt. Es wurde festgestellt, dass es zwei mögliche Typen für eine 32-Bit-Ganzzahl gibt: int und Int32. Der Typ int ist ein C#-Typ. Int32 ist eine Struktur, die zu System gehört. Ihr vollständiger Name lautet System.Int32. Der Typ int ist ein C#-Alias für die .NET-Struktur System.Int32. Ebenso ist die C#-Zeichenkette ein Alias für den .NET-Typ System.String. System.String ist eine Klasse und keine Struktur. Die beiden Konzepte sind ähnlich, weisen jedoch den folgenden grundlegenden Unterschied auf:

  • Eine Variable vom Typ Struktur kann über ihren Wert manipuliert werden
  • Eine Variable vom Typ „Class“ kann über ihre Adresse (Referenz in der Objektsprache) manipuliert werden.

Eine Struktur wie eine Klasse sind komplexe Typen mit Attributen und Methoden. Zum Beispiel können wir schreiben:

string nomDuType=3.GetType().FullName;

Oben ist das Literal 3 in C# standardmäßig vom Typ int, Typ .NET System.Int32. Diese Struktur verfügt über eine GetType()-Methode, die ein Objekt zurückgibt, das die Eigenschaften des Datentyps System.Int32 kapselt. Dazu gehört die Eigenschaft FullName, die den vollständigen Namen des Typs zurückgibt. Wie Sie sehen können, ist das Literal 3 komplexer, als es auf den ersten Blick erscheint.

Hier ist ein Programm, das diese Punkte veranschaulicht:


using System;
 
namespace Chap1 {
    class P00 {
        static void Main(string[] args) {
             // example 1
            int ent = 2;
            float fl = 10.5F;
            double d = -4.6;
            string s = "essai";
            uint ui = 5;
            long l = 1000;
            ulong ul = 1001;
            byte octet = 5;
            short sh = -4;
            ushort ush = 10;
            decimal dec = 10.67M;
            bool b = true;
            Console.WriteLine("Type de ent[{1}] : [{0},{2}]", ent.GetType().FullName, ent,sizeof(int));
            Console.WriteLine("Type de fl[{1}]: [{0},{2}]", fl.GetType().FullName, fl, sizeof(float));
            Console.WriteLine("Type de d[{1}] : [{0},{2}]", d.GetType().FullName, d, sizeof(double));
            Console.WriteLine("Type de s[{1}] : [{0}]", s.GetType().FullName, s);
            Console.WriteLine("Type de ui[{1}] : [{0},{2}]", ui.GetType().FullName, ui, sizeof(uint));
            Console.WriteLine("Type de l[{1}] : [{0},{2}]", l.GetType().FullName, l, sizeof(long));
            Console.WriteLine("Type de ul[{1}] : [{0},{2}]", ul.GetType().FullName, ul, sizeof(ulong));
            Console.WriteLine("Type de b[{1}] : [{0},{2}]", octet.GetType().FullName, octet, sizeof(byte));
            Console.WriteLine("Type de sh[{1}] : [{0},{2}]", sh.GetType().FullName, sh, sizeof(short));
            Console.WriteLine("Type de ush[{1}] : [{0},{2}]", ush.GetType().FullName, ush, sizeof(ushort));
            Console.WriteLine("Type de dec[{1}] : [{0},{2}]", dec.GetType().FullName, dec, sizeof(decimal));
            Console.WriteLine("Type de b[{1}] : [{0},{2}]", b.GetType().FullName, b, sizeof(bool));
        }
    }
}
  • Zeile 7: Deklaration einer Integer-Variablen
  • Zeile 19: Der Typ einer Variablen v kann mit v.GetType().FullName ermittelt werden. Die Größe einer S-Struktur kann mit sizeof(S) ermittelt werden. Die Anweisung Console.WriteLine("... {0} ... {1} ...", param0, param1, ...) schreibt den Text des ersten Parameters auf den Bildschirm und ersetzt dabei jede {i}-Notation durch den Wert des Ausdrucks parami.
  • Zeile 22: Der Typ-String-Operator kann nicht verwendet werden, da er eine Klasse und keine Struktur bezeichnet. sizeof.

Hier ist das Ergebnis:

Type de ent[2] : [System.Int32,4]
Type de fl[10,5]: [System.Single,4]
Type de d[-4,6] : [System.Double,8]
Type de s[essai] : [System.String]
Type de ui[5] : [System.UInt32,4]
Type de l[1000] : [System.Int64,8]
Type de ul[1001] : [System.UInt64,8]
Type de b[5] : [System.Byte,1]
Type de sh[-4] : [System.Int16,2]
Type de ush[10] : [System.UInt16,2]
Type de dec[10,67] : [System.Decimal,16]
Type de b[True] : [System.Boolean,1]

Die Anzeige gibt .NET-Typen wieder, keine C#-Aliase.

3.2.2. Notation von Literalwerten

Ganzzahl int (32 Bit)
145, -7, 0xFF (hexadezimal)
Ganzzahl long (64 Bit) – Suffix L
100000L
Gleitkomma double
134,789, -45E-18 (-45 × 10⁻¹⁸)
Gleitkomma (Suffix F)
134,789F, -45E-18F (-45 × 10⁻¹⁸)
reelle Dezimalzahl (Suffix M)
100000M
Zeichen char
'A', 'b'
Zeichenkette string
"today" "c:\Kap1\Absatz3" @"c:\Kap1\Absatz3"
Boolescher Wert bool
true, false
Datum
new DateTime(1954,10,13) (Jahr, Monat, Tag) für den 13.10.1954

Beachten Sie die beiden Literalzeichenfolgen: „c:\chap1\\paragraph3“ und @"c:\chap1\paragraph3". In Literalzeichenfolgen wird das Zeichen \ interpretiert. Somit steht „\n“ für das Zeilenendezeichen und nicht für die Folge der beiden Zeichen \ und n. Wenn wir diese Folge wünschen, müssten wir „\\n“ schreiben, wobei die Folge als ein einzelnes \-Zeichen interpretiert wird. Sie könnten auch @"\n" schreiben, um das gleiche Ergebnis zu erhalten. Die Syntax @"text" erfordert, dass der Text genau so übernommen wird, wie er geschrieben ist. Dies wird manchmal als wörtliche Zeichenfolge bezeichnet.

3.2.3. Datenausgabe

3.2.3.1. Die Rolle von Deklarationen

Ein Programm verarbeitet Daten, die durch einen Namen und einen Typ gekennzeichnet sind. Diese Daten werden im Speicher abgelegt. Bei der Übersetzung des Programms weist der Compiler jedem Datenelement einen Speicherplatz zu, der durch eine Adresse und eine Größe gekennzeichnet ist. Dies geschieht mithilfe von Deklarationen, die vom Programmierer vorgenommen werden.

Sie ermöglichen es dem Compiler zudem, Programmierfehler zu erkennen. Zum Beispiel:

x=x*2;

wird als fehlerhaft deklariert, wenn x beispielsweise eine Zeichenkette ist.

3.2.3.2. Deklaration von Konstanten

Die Syntax zur Deklaration einer Konstante lautet wie folgt:

    const type nom=valeur;          //définit constante nom=valeur

Beispiel:

const float myPI=3.141592F;    

Warum Konstanten deklarieren?

  1. Das Programm ist leichter zu lesen, wenn die Konstante einen aussagekräftigen Namen erhält:
    const  float taux_tva=0.186F;
  1. Das Programm lässt sich leichter anpassen, wenn sich die „Konstante“ ändert. Wenn sich beispielsweise im vorigen Fall der Mehrwertsteuersatz auf 33 % ändert, muss lediglich die Anweisung geändert werden, die ihren Wert definiert:
    const float taux_tva=0.33F;

Wäre 0,186 explizit im Programm verwendet worden, hätten viele Anweisungen geändert werden müssen.

3.2.3.3. Variablendeklaration

Eine Variable wird durch einen Namen identifiziert und bezieht sich auf einen Datentyp. In C# wird zwischen Groß- und Kleinschreibung unterschieden. Daher sind FIN und end unterschiedlich.

Variablen können bei ihrer Deklaration initialisiert werden. Die Syntax zum Deklarieren einer oder mehrerer Variablen lautet:

 Identificateur_de_type variable1[=valeur1],variable2=[valeur2],...;

wobei Typbezeichner entweder ein vordefinierter Typ oder ein vom Programmierer definierter Typ ist. Optional kann eine Variable sowohl deklariert als auch initialisiert werden.

Sie können auch die Angabe des genauen Typs einer Variablen vermeiden, indem Sie anstelle von Typbezeichner das Schlüsselwort var verwenden:

 var variable1=valeur1,variable2=valeur2,...;

Das Schlüsselwort „var“ bedeutet nicht, dass Variablen keinen bestimmten Typ haben. Der Variablen „variablei“ wird der Datentyp „valuei“ zugewiesen. Die Initialisierung ist hier obligatorisch, damit der Compiler den Typ der Variablen ableiten kann.

Hier ein Beispiel:


using System;
 
namespace Chap1 {
    class P00 {
        static void Main(string[] args) {
            int i=2;
            Console.WriteLine("Type de int i=2 : {0},{1}",i.GetType().Name,i.GetType().FullName);
            var j = 3;
            Console.WriteLine("Type de var j=3 : {0},{1}", j.GetType().Name, j.GetType().FullName);
            var aujourdhui = DateTime.Now;
            Console.WriteLine("Type de var aujourdhui : {0},{1}", aujourdhui.GetType().Name, aujourdhui.GetType().FullName);
        }
    }
}
  • Zeile 6: explizit typisierte Daten
  • Zeile 7: (data).GetType().Name ist der Kurzname für (data), (data).GetType().FullName ist der vollständige Name von (data)
  • Zeile 8: implizit typisierte Daten. Da 3 vom Typ int ist, ist j vom Typ int.
  • Zeile 10: implizit typisierte Daten. Da der Typ von DateTime.Now DateTime ist, ist der Typ von today ebenfalls DateTime.

Bei der Ausführung erhalten wir das folgende Ergebnis:

1
2
3
Type de int i=2 : Int32,System.Int32
Type de var j=3 : Int32,System.Int32
Type de var aujourdhui : DateTime,System.DateTime

Eine Variable, die implizit durch das Schlüsselwort var typisiert wurde, kann ihren Typ anschließend nicht mehr ändern. Beispielsweise folgt nach Zeile 10 des Codes die Zeile:


            var aujourdhui = "aujourd'hui";

Wir werden später sehen, dass es möglich ist, einen Typ „on the fly“ in einem Ausdruck zu deklarieren. In diesem Fall handelt es sich um einen anonymen Typ, dem der Benutzer keinen Namen gegeben hat. Der Compiler wird diesem neuen Typ einen Namen geben. Wenn ein anonymer Typ einer Variablen zugewiesen werden soll, ist die einzige Möglichkeit, ihn zu deklarieren, die Verwendung des Schlüsselworts var.

3.2.4. Konvertierungen zwischen Zahlen und Zeichenketten

Zahl -> Zeichenkette
nombre.ToString()
Zeichenkette -> int
int.Parse(string) oder System.Int32.Parse
Zeichenkette -> long
long.Parse(string) oder System.Int64.Parse
Zeichenkette -> double
double.Parse(string) oder System.Double.Parse(string)
Zeichenkette -> float
float.Parse(string) oder System.Float.Parse(string)

Die Konvertierung einer Zeichenfolge in eine Zahl kann fehlschlagen, wenn die Zeichenfolge keine gültige Zahl darstellt. Ein schwerwiegender Fehler namens exception. Dieser Fehler kann mit try/catch wie folgt behandelt werden:


try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Wenn die Funktion keine Ausnahme auslöst, fahren wir mit der nächsten Anweisung fort; andernfalls springen wir in den Hauptteil der catch-Klausel und anschließend zur nächsten Anweisung. Wir werden später noch auf die Ausnahmebehandlung zurückkommen. Hier ist ein Programm, das einige Techniken zur Konvertierung zwischen Zahlen und Zeichenketten veranschaulicht. In diesem Beispiel schreibt die Funktion poster den Wert ihres Parameters auf den Bildschirm. Somit schreibt poster(S) den Wert von S auf den Bildschirm, wobei S vom Typ Zeichenkette ist.


using System;
 
namespace Chap1 {
    class P01 {
        static void Main(string[] args) {
 
             // data
            const int i = 10;
            const long l = 100000;
            const float f = 45.78F;
            double d = -14.98;
 
             // number --> string
            affiche(i.ToString());
            affiche(l.ToString());
            affiche(f.ToString());
            affiche(d.ToString());
 
             //boolean --> string
            const bool b = false;
            affiche(b.ToString());
 
             // string --> int
            int i1;
            i1 = int.Parse("10");
            affiche(i1.ToString());
            try {
                i1 = int.Parse("10.67");
                affiche(i1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // chain --> long
            long l1;
            l1 = long.Parse("100");
            affiche(l1.ToString());
            try {
                l1 = long.Parse("10.675");
                affiche(l1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // chain --> double
            double d1;
            d1 = double.Parse("100,87");
            affiche(d1.ToString());
            try {
                d1 = double.Parse("abcd");
                affiche(d1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // string --> float
            float f1;
            f1 = float.Parse("100,87");
            affiche(f1.ToString());
            try {
                d1 = float.Parse("abcd");
                affiche(f1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
         }// fine hand
 
        public static void affiche(string S) {
            Console.Out.WriteLine("S={0}",S);
        }
     }// end of class
}

Die Zeilen 30–32 behandeln die mögliche Ausnahme, die auftreten kann. e.Message ist die Fehlermeldung, die mit der Ausnahme e verknüpft ist.

Die Ergebnisse lauten wie folgt:

S=10
S=100000
S=45,78
S=-14,98
S=False
S=10
S=Erreur : Input string was not in a correct format.
S=100
S=Erreur : Input string was not in a correct format.
S=100,87
S=Erreur : Input string was not in a correct format.
S=100,87
S=Erreur : Input string was not in a correct format.

Beachten Sie, dass bei reellen Zahlen in Zeichenfolgenform ein Komma und kein Dezimalpunkt verwendet werden muss. Daher schreiben wir

double d1=10.7; 

aber

double d2=int.Parse("10,7");

3.2.5. Datentabellen

Ein C#-Array ist ein Objekt, das dazu dient, Daten desselben Typs unter einem gemeinsamen Bezeichner zu gruppieren. Seine Deklaration lautet wie folgt:

Type[] tableau[]=new Type[n]

n ist die Anzahl der Datenelemente, die das Array enthalten kann. Die Syntax Table[i] bezeichnet das Datenelement Nr. i, wobei i zum Intervall [0,n-1] gehört. Jeder Verweis auf das Datenelement Table[i], bei dem i nicht zum Intervall [0,n-1] gehört, löst eine Ausnahme aus. Ein Array kann sowohl initialisiert als auch deklariert werden:

    int[] entiers=new int[] {0,10,20,30};

oder einfach:

    int[] entiers={0,10,20,30};

Arrays haben eine Eigenschaft namens „Length“, die die Anzahl der Elemente im Array angibt.

Ein zweidimensionales Bild kann wie folgt deklariert werden:

Type[,] array = new Type[n, m];

wobei n die Anzahl der Zeilen und m die Anzahl der Spalten ist. Die Syntax Table[i,j] bezeichnet das Element j in Zeile i der Tabelle. Das zweidimensionale Array kann auch gleichzeitig mit seiner Deklaration initialisiert werden:

    double[,] réels=new double[,] { {0.5, 1.7}, {8.4, -6}};

oder einfach:

    double[,] réels={ {0.5, 1.7}, {8.4, -6}};

Die Anzahl der Elemente in jeder Dimension kann mit GetLength(i) ermittelt werden, wobei i=0 die dem ersten Index entsprechende Dimension, i=1 die dem zweiten Index entsprechende Dimension usw. darstellt.

Die Gesamtzahl der Dimensionen erhält man mit der Eigenschaft Rank, die Gesamtzahl der Elemente mit Length.

Eine Tabelle von Tabellen wird wie folgt deklariert:

Type[][] array = new Type[n][];

Die obige Anweisung erstellt ein Array mit n Zeilen. Jedes Element table[i] ist eine Referenz auf ein eindimensionales Array. Diese Referenzen array[i] werden in der obigen Deklaration nicht initialisiert. Ihr Wert ist die Referenz null.

Das folgende Beispiel veranschaulicht die Erstellung eines Arrays von Tabellen:


            // un tableau de tableaux
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//for
            // initialisation
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    noms[i][j] = "nom" + i + j;
                }//for j
            }//for i
  • Zeile 2: Eine Tabelle „names“ vom Typ string[][]. Jedes Element ist ein Array-Zeiger (eine Objektreferenz), dessen Elemente vom Typ string[] sind.
  • Zeilen 3–5: Die 3 Elemente der Tabelle names werden initialisiert. Jedes „zeigt“ nun auf ein Array von Elementen vom Typ string[]. names[i][j] ist das Array vom Typ string[] mit der Indexnummer j, auf das names[i] verweist.
  • Zeile 9: Initialisierung des Elements names[i][j] innerhalb einer doppelten Schleife. Hier ist names[i] ein Array mit i+1 Elementen. Da names[i] ein Array ist, ist names[i].Length die Anzahl seiner Elemente.

Hier ist ein Beispiel für die drei Tabellentypen, die wir gerade vorgestellt haben:


using System;
 
namespace Chap1 {
     // tables
 
    using System;
 
     // test class
    public class P02 {
        public static void Main() {
             // an initialized 1-dimensional array
            int[] entiers = new int[] { 0, 10, 20, 30 };
            for (int i = 0; i < entiers.Length; i++) {
                Console.Out.WriteLine("entiers[{0}]={1}", i, entiers[i]);
            }//for
 
             // an initialized 2-dimensional array
            double[,] réels = new double[,] { { 0.5, 1.7 }, { 8.4, -6 } };
            for (int i = 0; i < réels.GetLength(0); i++) {
                for (int j = 0; j < réels.GetLength(1); j++) {
                    Console.Out.WriteLine("réels[{0},{1}]={2}", i, j, réels[i, j]);
                }//for j
            }//for i
 
             // a table of tables
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//for
             // initialization
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    noms[i][j] = "nom" + i + j;
                }//for j
            }//for i
             // display
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    Console.Out.WriteLine("noms[{0}][{1}]={2}", i, j, noms[i][j]);
                }//for j
            }//for i
         }//Main
     }//class
}//namespace

Bei der Ausführung erhalten wir folgende Ergebnisse:

entiers[0]=0
entiers[1]=10
entiers[2]=20
entiers[3]=30
réels[0,0]=0,5
réels[0,1]=1,7
réels[1,0]=8,4
réels[1,1]=-6
noms[0][0]=nom00
noms[1][0]=nom10
noms[1][1]=nom11
noms[2][0]=nom20
noms[2][1]=nom21
noms[2][2]=nom22

3.3. Grundlegende C#-Anweisungen

Es wird unterschieden zwischen

1 die vom Computer ausgeführten elementaren Befehle.

2 Befehle zur Steuerung des Programmablaufs.

Die elementaren Befehle werden verständlich, wenn man die Struktur eines Mikrocomputers und seiner Peripheriegeräte betrachtet.

  1. Einlesen von Informationen von der Tastatur

  2. Verarbeitung von Informationen

  3. Informationen auf den Bildschirm schreiben

3.3.1. Anzeigen auf dem Bildschirm

Es gibt verschiedene Befehle zum Schreiben auf dem Bildschirm:

Console.Out.WriteLine(expression)
Console.WriteLine(expression)
Console.Error.WriteLine (expression)

wobei Ausdruck ein beliebiger Datentyp ist, der zur Anzeige auf dem Bildschirm in eine Zeichenfolge konvertiert werden kann. Alle C#- oder .NET-Objekte verfügen über eine ToString()-Methode, die zur Durchführung dieser Konvertierung verwendet wird.

Die Klasse System.Console bietet Zugriff auf Bildschirmausgabeoperationen (Write, WriteLine). Die Klasse Console verfügt über zwei Eigenschaften, Out und Error, die vom Typ TextWriter sind:

  • Console.WriteLine() entspricht Console.Out.WriteLine() und schreibt in den Out-Stream, der normalerweise mit dem Bildschirm verbunden ist.
  • Console.Error.WriteLine() schreibt in den Stream Error, der normalerweise mit dem Bildschirm verbunden ist.

Die Streams „Out“ und „Error“ können zur Laufzeit des Programms in Textdateien umgeleitet werden, wie wir gleich sehen werden.

3.3.2. Lesen von eingegebenen Daten

Der Datenstrom von der Tastatur wird durch das Objekt Console.In vom Typ TextReader dargestellt. Mit diesem Objekttyp kann eine Textzeile mithilfe von ReadLine gelesen werden:

    string ligne=Console.In.ReadLine();

Die Klasse „Console“ bietet eine Methode „ReadLine“, die standardmäßig mit „In“ verknüpft ist. Wir können daher schreiben:

    string ligne=Console.ReadLine();

Die über die Tastatur eingegebene Zeile wird in der Variablen „ligne“ gespeichert und kann anschließend vom Programm verwendet werden. Der Stream „In“ kann wie „Out“ und „Error“ in eine Datei umgeleitet werden.

3.3.3. Beispiel für Ein- und Ausgabe

Hier ist ein kurzes Programm zur Veranschaulichung von Tastatur-/Bildschirm-E/A-Operationen:


using System;
 
namespace Chap1 {
     // test class
    public class P03 {
        public static void Main() {
 
             // write to Out feed
            object obj = new object();
            Console.Out.WriteLine(obj);
 
             // write to the Error stream
            int i = 10;
            Console.Error.WriteLine("i=" + i);
 
             // reading a line entered on the keyboard
            Console.Write("Tapez une ligne : ");
            string ligne = Console.ReadLine();
            Console.WriteLine("ligne={0}", ligne);
         }//fine hand
     }//end of class
}
  • Zeile 9: obj ist eine Objektreferenz
  • Zeile 10: obj wird auf den Bildschirm geschrieben. Standardmäßig wird obj.ToString() aufgerufen.
  • Zeile 14: Sie können auch schreiben:

            Console.Error.WriteLine("i={0}",i);

Der erste Parameter „i={0}“ ist das Anzeigeformat, die anderen Parameter sind die anzuzeigenden Ausdrücke. Die {n}-Elemente sind „Positionsparameter“. Zur Laufzeit wird der {n}-Parameter durch den Wert des Ausdrucks Nr. n ersetzt.

Das Ergebnis lautet wie folgt:

1
2
3
4
System.Object
i=10
Tapez une ligne : je suis là
ligne=je suis là
  • Zeile 1: Die Anzeige, die durch Zeile 10 des Codes erzeugt wurde. Die Methode obj.ToString() hat den Typnamen der Variablen obj angezeigt: System.Object. Der Typ „object“ ist ein C#-Alias für den .NET-Typ System.Object.

3.3.4. E/A-Umleitung

Unter DOS und UNIX gibt es drei Standardgeräte namens:

  1. Standard-Eingabegerät – standardmäßig die Tastatur und mit der Nummer 0
  2. Standardausgabegerät – standardmäßig der Bildschirm, Nummer 1
  3. Standard-Ausgabegerät – standardmäßig der Bildschirm und mit der Nummer 2

In C# schreibt Console.Out auf Gerät 1, der Schreibstrom Console.Error wird auf Gerät 2 geschrieben und der Lesestrom Console.In liest Daten von Gerät 0.

Wenn Sie ein Programm unter DOS oder Unix ausführen, können Sie festlegen, welche Geräte 0, 1 und 2 für das ausgeführte Programm verwendet werden sollen. Betrachten Sie die folgende Befehlszeile:

pg arg1 arg2 .. argn

Hinter den Argumenten des Programms „argi pg“ können Standard-E/A-Geräte in Dateien umgeleitet werden:

0<in.txt
Der Standard-Eingabestrom Nr. 0 wird in die Datei in.txt umgeleitet. Im Programm bezieht Console.In daher seine Daten aus der Datei in.txt.
1>out.txt
leitet die Ausgabe Nr. 1 in die Datei „out.txt“ um. Das bedeutet, dass im Programm „Console.Out“ seine Daten in die Datei „out.txt“ schreibt
1>>out.txt
ebenso, aber die geschriebenen Daten werden an den aktuellen Inhalt der Datei „out.txt“ angehängt.
2>error.txt
leitet die Ausgabe Nr. 2 in die Datei error.txt um. Das bedeutet, dass im Programm die Console.Error ihre Daten in die Datei error.txt schreibt
2>>error.txt
dito, aber die geschriebenen Daten werden an den aktuellen Dateiinhalt von error.txt angehängt.
1>out.txt 2>error.txt
Die Geräte 1 und 2 werden beide umgeleitet zu

Beachten Sie, dass zur Umleitung von E/A-Strömen vom Programm in Dateien das Programm nicht geändert werden muss. Das Betriebssystem bestimmt die Art der Peripheriegeräte 0, 1 und 2. Betrachten Sie das folgende Programm:


using System;
 
namespace Chap1 {
 
     // redirections
    public class P04 {
        public static void Main(string[] args) {
            // lecture flux In
            string data = Console.In.ReadLine();
             // write Out feed
            Console.Out.WriteLine("écriture dans flux Out : " + data);
            // écriture flux Error
            Console.Error.WriteLine("écriture dans flux Error : " + data);
         }//Main
     }//class
}

Erstellen wir nun die ausführbare Datei dieses Quellcodes:

  • in [1]: Die ausführbare Datei wird durch einen Rechtsklick auf das Projekt / Build erstellt
  • in [2]: In einem DOS-Fenster wurde die ausführbare Datei 04.exe im Ordner bin/Release des Projekts erstellt.

Geben wir die folgenden Befehle im DOS-Fenster [2] ein:

1
2
3
4
5
6
7
8
...\04\bin\Release>echo test >in.txt
...\04\bin\Release>more in.txt
test
...\04\bin\Release>04 0<in.txt 1>out.txt 2>err.txt
...\04\bin\Release>more out.txt
écriture dans flux Out : test
...\04\bin\Release>more err.txt
écriture dans flux Error : test
  • Zeile 1: Die Zeichenfolge „test“ in der Datei in.txt
  • Zeilen 2–3: Anzeige des Inhalts der Datei in.txt zur Überprüfung
  • Zeile 4: Programmausführung 04.exe. Der In-Stream wird in die Datei in.txt umgeleitet, der Out-Stream in die Datei out.txt, der Error-Stream in die Datei err.txt. Die Ausführung führt zu keiner Anzeige.
  • Zeilen 5–6: Inhalt der Datei „out.txt“. Dieser Inhalt zeigt uns, dass:
  • die Datei „in.txt“ gelesen wurde
  • die Bildschirmanzeige in die Datei „out.txt“ umgeleitet wurde
  • Zeilen 7–8: Ähnliche Überprüfung für die Datei „err.txt“

Wir können deutlich sehen, dass die Flows „Out“ und „In“ nicht auf dieselben Geräte schreiben, da sie separat umgeleitet wurden.

3.3.5. Zuweisung des Werts eines Ausdrucks an eine Variable

Uns interessiert hier die Operation variable=ausdruck;

Der Ausdruck kann vom Typ sein: arithmetisch, relational, boolesch, Zeichen

3.3.5.1. Interpretation der Zuweisungsoperation

Die Operation variable=ausdruck;

ist selbst ein Ausdruck, dessen Auswertung wie folgt erfolgt:

  • Die rechte Seite der Zuweisung wird ausgewertet: Das Ergebnis ist ein V-Wert.
  • Der Wert V wird der Variablen zugewiesen
  • Der Wert V ist auch der Wert der Zuweisung, diesmal als Ausdruck betrachtet.

So funktioniert die

    V1=V2=expression

zulässig ist. Aufgrund der Auswertungsreihenfolge wird der =-Operator ganz rechts ausgewertet. Wir erhalten daher

    V1=(V2=expression)

Der Ausdruck V2=Ausdruck wird ausgewertet und erhält den Wert V. Durch die Auswertung dieses Ausdrucks wurde V an V2 zugewiesen. Der folgende =-Operator wird dann wie folgt ausgewertet:

    V1=V

Der Wert dieses Ausdrucks ist weiterhin V. Durch seine Auswertung wird V an V1 zugewiesen.

Der Ausdruck V1=V2=

ist ein Ausdruck, dessen Auswertung

  • den Wert von Ausdruck in die Variablen V1 und V2
  • den Wert des Ausdrucks ergibt.

Dies lässt sich auf einen Ausdruck wie den folgenden verallgemeinern:

V1=V2=....=Vn=expression

3.3.5.2. Arithmetischer Ausdruck

Die Operatoren für arithmetische Ausdrücke lauten wie folgt:

  • Addition

  • Subtraktion

* Multiplikation

/ Division: Das Ergebnis ist der exakte Quotient, wenn mindestens einer der Operanden reell ist. Sind beide Operanden ganzzahlig, ist das Ergebnis der ganze Quotient. Somit ergibt 5/2 -> 2 und 5,0/2 -> 2,5.

% Division: Das Ergebnis ist der Rest, unabhängig von der Art der Operanden, wobei der Quotient eine ganze Zahl ist. Es handelt sich also um die Modulo-Operation.

Es gibt verschiedene mathematische Funktionen. Hier sind einige davon:

double Sqrt(double x)
Quadratwurzel
double Cos(double x)
Cosinus
double Sin(double x)
Sinus
double Tan(double x)
Tangens
double Pow(double x, double y)
x hoch y (x > 0)
double Exp(double x)
Exponential
double Log(double x)
Neper-Logarithmus
double Abs(double x)
Absolutwert

usw...

All diese Funktionen sind in einer C#-Klasse namens Math definiert. Bei der Verwendung muss ihnen der Name der Klasse vorangestellt werden, in der sie definiert sind. Schreiben Sie zum Beispiel:

double x, y=4;
x=Math.Sqrt(y);

Die vollständige Definition von Math lautet wie folgt:

Image

Image

Image

Image

Image

3.3.5.3. Prioritäten bei der Auswertung arithmetischer Ausdrücke

Die Priorität der Operatoren bei der Auswertung eines arithmetischen Ausdrucks ist wie folgt (von der höchsten zur niedrigsten Priorität):

[fonctions], [ ( )],[ *, /, %], [+, -]

Operatoren im selben [ ]-Block haben die gleiche Priorität.

3.3.5.4. Relationale Ausdrücke

Es gibt folgende Operatoren:

       <, <=, ==, !=, >, >=

Prioritäten der Operatoren

>, >=, <, <=
==, !=

Das Ergebnis eines relationalen Ausdrucks ist der Boolesche Wert „false“, wenn der Ausdruck falsch ist, „true“ oder „else“.

      bool fin;
      int x=...;
      fin=x>4;

Vergleich zweier Merkmale

Betrachten wir zwei Variablen C1 und C2. Sie können mithilfe der Operatoren verglichen werden

    <, <=, ==, !=, >, >=

verglichen werden. Dabei werden ihre Unicode-Codes, die Zahlen sind, miteinander verglichen. In der Unicode-Reihenfolge gelten folgende Beziehungen:

espace < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. <'z'

Vergleich zweier Zeichenfolgen

Sie werden Zeichen für Zeichen verglichen. Die erste Ungleichheit, auf die zwischen zwei Zeichen gestoßen wird, führt zu einer Ungleichheit mit derselben Bedeutung für die Zeichenfolgen.

Beispiele:

Vergleichen Sie entweder die Zeichenfolgen „Cat“ und „Dog“

Diese letzte Ungleichung bedeutet, dass „Katze“ < „Hund“ ist.

Vergleiche entweder die Ketten „Katze“ und „Kätzchen“. Es herrscht immer Gleichstand, bis die Kette „Katze“ erschöpft ist. In diesem Fall wird die erschöpfte Kette als die „kleinste“ deklariert. Wir haben daher die Beziehung

    "Chat" < "Chaton".

Funktionen zum Vergleichen zweier Zeichenfolgen

Sie können die Vergleichsoperatoren == und != verwenden, um die Gleichheit zweier Zeichenfolgen zu prüfen, oder die Klasse System.String*.*Equals*. Für die Beziehungen <, <=, > und >= verwenden Sie die Klasse System.String.CompareTo* ( ):


using System;
 
namespace Chap1 {
    class P05 {
        static void Main(string[] args) {
            string chaine1="chat", chaine2="chien";
            int n = chaine1.CompareTo(chaine2);
            bool egal = chaine1.Equals(chaine2);
            Console.WriteLine("i={0}, egal={1}", n, egal);
            Console.WriteLine("chien==chaine1:{0},chien!=chaine2:{1}", "chien"==chaine1,"chien" != chaine2);
        }
    }
}

Zeile 7: Die Variable i hat den Wert:

    0    , wenn beide Zeichenfolgen gleich sind

    1    wenn Kanal 1 &gt; Kanal 2

    -1    wenn Kette Nr. 1 &lt; Kette Nr. 2

In Zeile 8 hat die Variable „egal“ den Wert „true“, wenn beide Zeichenfolgen gleich sind, andernfalls „false“. In Zeile 10 wird mit den Operatoren „==“ und „!=“ geprüft, ob zwei Zeichenfolgen gleich sind oder nicht.

Ausführungsergebnisse:

i=-1, egal=False
chien==chaine1:False,chien!=chaine2:False

3.3.5.5. Boolesche Ausdrücke

Die Operatoren, die verwendet werden können, sind AND (&&), OR (||) und NOT (!). Das Ergebnis eines booleschen Ausdrucks ist ein Boolescher Wert.

Operatorprioritäten:

  1. !
  2. &&
  3. ||

            double x = 3.5;
            bool valide = x > 2 && x < 4;

Relationale Operatoren haben die Priorität der Operatoren && und ||.

3.3.5.6. Bitoperationen

Operatoren

Seien i und j zwei ganze Zahlen.

i<<n
verschiebt i um n Bits nach links. Die eingehenden Bits sind Nullen.
i>>n
verschiebt i um n Bits nach rechts. Wenn i eine vorzeichenbehaftete Ganzzahl ist (signed char, int, long), bleibt das Vorzeichenbit erhalten.
i & j
führt die logische UND-Verknüpfung von i und j Bit für Bit durch.
i | j
führt die logische ODER-Verknüpfung von i und j bitweise durch.
~i
komplementiert i zu 1
i^j
führt die exklusive ODER-Verknüpfung von i und j durch

Oder der folgende Code:


short i = 100, j = -13;
ushort k = 0xF123;
Console.WriteLine("i=0x{0:x4}, j=0x{1:x4}, k=0x{2:x4}", i,j,k);
Console.WriteLine("i<<4=0x{0:x4}, i>>4=0x{1:x4},k>>4=0x{2:x4},i&j=0x{3:x4},i|j=0x{4:x4},~i=0x{5:x4},j<<2=0x{6:x4},j>>2=0x{7:x4}", i << 4, i >> 4, k >> 4, (short)(i & j), (short)(i | j), (short)(~i), (short)(j << 2), (short)(j >> 2));
  • format {0:x4} zeigt Parameter Nr. 0 im Hexadezimalformat (x) mit 4 Zeichen (4) an.

Die Ergebnisse lauten wie folgt:

i=0x0064, j=0xfff3, k=0xf123
i<<4=0x0640, i>>4=0x0006,k>>4=0x0f12,i&j=0x0060,i|j=0xfff7,~i=0xff9b,j<<2=0xffcc,j>>2=0xfffc

3.3.5.7. Operatorkombination

a=a+b kann als a+=b geschrieben werden

a=a-b kann als a-=b geschrieben werden

Das Gleiche gilt für die Operatoren /, %, *, <<, >>, &, |, ^. So kann a=a/2; als a/=2; geschrieben werden

3.3.5.8. Inkrement- und Dekrement-Operatoren

Die Bewertung von variable++ bedeutet variable=variable+1 oder auch variable+=1

Die Bewertung von variable-- bedeutet variable=variable-1 oder auch variable-=1.

3.3.5.9. Der ternäre Operator?

Der Ausdruck

    expr_cond ? expr1:expr2

wird wie folgt ausgewertet:

1 wird der Ausdruck expr_cond ausgewertet. Es handelt sich um einen bedingten Ausdruck mit einem echten oder falschen

2 Ist er wahr, ist der Wert des Ausdrucks der von expr1, und expr2 wird nicht ausgewertet.

3 Ist er falsch, geschieht das Gegenteil: Der Wert des Ausdrucks ist der von expr2 und expr1 wird nicht ausgewertet.

Die Operation i=(j>4 ? j+1:j-1); wird der Variablen i zugewiesen: j+1, wenn j>4, andernfalls j-1. Dies entspricht der Schreibweise if(j>4) i=j+1; else i=j-1;, ist jedoch prägnanter.

3.3.5.10. Allgemeine Priorität der Operatoren

() []  Funktion
gd
! ~ ++ --
dg
new (Typ) Operatoren cast
dg
*  /  %
gd
+  -
gd
<<  >>
gd
< <=  > >= instanceof
gd
==    !=
gd
&
gd
^
gd
|
gd
&&
gd
||
gd
?   :
dg
= += -= usw. .
dg

gd gibt an, dass bei gleicher Priorität die Links-Rechts-Regel gilt. Das bedeutet, dass bei gleichrangigen Operatoren in einem Ausdruck der Operator ganz links im Ausdruck zuerst ausgewertet wird. dg gibt die Rechts-Links-Regel an.

3.3.5.11. Typänderungen

In einem Ausdruck können Sie die Kodierung eines Werts vorübergehend ändern. Dies wird als Änderung des Datentyps oder Typumwandlung bezeichnet. Die Syntax zum Ändern des Typs eines Werts in einem Ausdruck lautet wie folgt:

    (type) valeur

Der Wert nimmt dann den angegebenen Typ an. Dies führt zu einer Änderung der Kodierung des Werts.


using System;
 
namespace Chap1 {
    class P06 {
        static void Main(string[] args) {
            int i = 3, j = 4;
            float f1=i/j;
            float f2=(float)i/j;
            Console.WriteLine("f1={0}, f2={1}",f1,f2);
        }
    }
}
  • Zeile 7: f1 hat den Wert 0,0. Die Division durch 3/4 ist eine ganzzahlige Division, da beide Operanden vom Typ int sind.
  • Zeile 8: (float)i ist der in float umgewandelte Wert von i. Nun haben wir eine Division zwischen einer reellen Zahl vom Typ float und einer Ganzzahl vom Typ int. Dies ist die Division zwischen reellen Zahlen. Der Wert von j wird ebenfalls in einen float umgewandelt, dann werden die beiden reellen Zahlen dividiert. f2 hat dann den Wert 0,75.

Hier sind die Ergebnisse:

f1=0, f2=0,75

In (float)i:

  • i ist ein exakt 2-Byte-codierter Wert
  • (float) i ist derselbe Wert, codiert als echte 4-Byte-Näherung

Der Wert von i. Diese Transkodierung findet nur für die Dauer einer Berechnung statt, und die Variable i behält stets ihren Typ int bei.

3.4. Anweisungen zur Programmsteuerung

3.4.1. Stopp

Der in der Umgebung definierte Befehl „Exit“ beendet die Programmausführung.

syntaxe        void Exit(int status)
action        arrête le processus en cours et rend la valeur status au processus père

Exit beendet den aktuellen Prozess und gibt die Kontrolle an den aufrufenden Prozess zurück. Der Wert von status kann von diesem verwendet werden. Unter DOS wird diese Statusvariable in der Systemvariablen ERRORLEVEL abgebildet, deren Wert in einer Batch-Datei überprüft werden kann. Unter Unix, mit dem Bourne-Shell-Befehlsinterpreter, gibt die Variable $? den Wert von status zurück.

    Environment.Exit(0);

beendet die Programmausführung mit einem Statuswert von 0.

3.4.2. Einfache Entscheidungsstruktur

 syntaxe :  if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}

Anmerkungen:

  • Die Bedingung steht in Klammern.
  • Jede Aktion wird durch ein Semikolon abgeschlossen.
  • Klammern werden nicht durch Semikolons abgeschlossen.
  • Klammern sind nur erforderlich, wenn mehr als eine Aktion vorhanden ist.
  • Die else-Klausel kann weggelassen werden.
  • Es gibt keine then-Klausel.

Das algorithmische Äquivalent dieser Struktur ist if .. then ... otherwise :

Beispiel


    if (x>0)  { nx=nx+1;sx=sx+x;} else dx=dx-x;

Entscheidungsstrukturen können verschachtelt werden:

if(condition1)
if (condition2)
        {......}
      else         //condition2
         {......}
else         //condition1
     {.......}

Das folgende Problem tritt manchmal auf:


using System;
 
namespace Chap1 {
    class P07 {
        static void Main(string[] args) {
            int n = 5;
            if (n > 1)
                if (n > 6)
                    Console.Out.WriteLine(">6");
                else Console.Out.WriteLine("<=6");
        }
    }
}

In dem vorherigen Beispiel, auf welches if bezieht sich das else in Zeile 10? Die Regel lautet, dass sich ein else immer auf das nächstgelegene if bezieht: im Beispiel if(n>6) in Zeile 8. Betrachten wir ein weiteres Beispiel:


            if (n2 > 1) {
                if (n2 > 6) Console.Out.WriteLine(">6");
       } else Console.Out.WriteLine("<=1");    

Hier wollten wir bei „if(n2>1)“ ein „else“ einfügen und bei „if(n2>6)“ kein „else“. Aufgrund der vorherigen Anmerkung müssen wir bei „if(n2>1) {...} else ...“ Klammern setzen.

3.4.3. Case-Struktur

Die Syntax lautet wie folgt:

switch(expression) {
    case v1:     
            actions1;
            break;
    case v2:     
            actions2;
            break;
         . .. .. .. .. ..
    default:     
            actions_sinon;
            break;
}

Anmerkungen

  • Der Wert des Schalters kann eine Ganzzahl, ein Zeichen oder eine Zeichenkette sein
  • Der Ausdruck steht in Klammern.
  • Die Klausel „default“ kann fehlen.
  • Die Werte vi sind mögliche Werte des Ausdrucks. Ist der Wert des Ausdrucks vi, werden die Aktionen hinter der Klausel case vi ausgeführt.
  • Die Anweisung break führt uns aus der case-Struktur heraus.
  • Jeder mit einem Wert vi verknüpfte Anweisungsblock muss mit einer Verzweigungsanweisung (break, goto, return, ...) enden, andernfalls meldet der Compiler einen Fehler.

Beispiel

Besuchen Sie Algorithmen

                selon la valeur de choix
                    cas 0
                         fin du module
                    cas 1
                         exécuter module M1
                    cas 2
                        exécuter module M2
                    sinon
                         erreur<--vrai
                findescas

In C#

int choix = 2; 
bool erreur = false;
switch (choix) {
   case 0: return;
   case 1: M1(); break;
   case 2: M2(); break;
   default: erreur = true; break;
}
}// end Main

static void M1() {
    Console.WriteLine("M1");
}

static void M2() {
    Console.WriteLine("M2");
}
}

3.4.4. Wiederholungsstrukturen

3.4.4.1. Bekannte Anzahl von Wiederholungen

Struktur für

Die Syntax lautet wie folgt:


    for (i=id;i<=if;i=i+ip){ 
       actions; 
      } 

Anmerkungen

  • Die drei Argumente von „for“ stehen in Klammern und sind durch Semikolons getrennt.
  • Jedes „for“ wird durch ein Semikolon abgeschlossen.
  • Die geschweifte Klammer ist nur erforderlich, wenn mehr als eine Aktion vorhanden ist.
  • Auf die geschweifte Klammer folgt kein Semikolon.

Das algorithmische Äquivalent ist die for-Schleife:

pour i variant de id à if avec un pas de ip
    actions
finpour

was wie folgt übersetzt werden kann:

    i  id
    tantque i<=if
        actions
        i i+ip
    fintantque

Struktur foreach

Die Syntax lautet wie folgt:


foreach (Type variable in collection)
    instructions; 
}

Hinweise

  • collection ist eine Sammlung von enumerierbaren Objekten. Die uns bereits bekannte Sammlung von enumerierbaren Objekten ist das Array
  • „Type“ ist der Typ der Objekte in der Sammlung. Bei einem Array wäre dies der Typ der Array-Elemente
  • variable ist eine lokale Variable der Schleife, die nacheinander alle Werte der Sammlung als ihren Wert annimmt.

Daher der folgende Code:


            string[] amis = { "paul", "hélène", "jacques", "sylvie" };
            foreach (string nom in amis) {
                Console.WriteLine(nom);
         }

würde Folgendes anzeigen:

paul
hélène
jacques
sylvie

3.4.4.2. Anzahl der Wiederholungen unbekannt

In C# gibt es für diesen Fall viele Strukturen.

Struktur „while“


    while(condition){
          actions;
        } 

Wir wiederholen die Schleife, solange die Bedingung erfüllt ist. Die Schleife wird möglicherweise nie ausgeführt.

Anmerkungen:

  • Die Bedingung steht in Klammern.
  • Jede Aktion wird durch ein Semikolon abgeschlossen.
  • Die geschweifte Klammer ist nur erforderlich, wenn mehr als eine Aktion vorhanden ist.
  • Auf die geschlossene Klammer folgt kein Semikolon.

Die entsprechende algorithmische Struktur lautet tantque :

tantque condition
    actions
fintantque

Wiederholstruktur bis (do while)

Die Syntax lautet wie folgt:


    do{
       instructions;
    }while(condition); 

Wir wiederholen die Schleife, bis die Bedingung falsch wird. In diesem Fall wird die Schleife mindestens einmal durchlaufen.

Anmerkungen

  • Die Bedingung steht in Klammern.
  • Jede Aktion wird durch ein Semikolon abgeschlossen.
  • Die geschweifte Klammer ist nur erforderlich, wenn mehr als eine Aktion vorhanden ist.
  • Auf die geschlossene Klammer folgt kein Semikolon.

Die entsprechende algorithmische Struktur lautet repeat ... until :

répéter
    actions
jusqu'à condition

Struktur für allgemeine (for)

Die Syntax lautet wie folgt:


for(instructions_départ;condition;instructions_fin_boucle){
    instructions; 
}

Die Schleife wird so lange ausgeführt, wie die Bedingung wahr ist (die vor jedem Schleifendurchlauf ausgewertet wird). Die Startanweisungen werden vor dem ersten Eintritt in die Schleife ausgeführt. Die Anweisungen am Schleifenende werden nach jedem Schleifendurchlauf ausgeführt.

Anmerkungen

  • Die verschiedenen Anweisungen in instructions_depart und instructions_fin_boucle werden durch Kommas getrennt.

Die entsprechende algorithmische Struktur sieht wie folgt aus:

instructions_départ
tantque condition
    actions
    instructions_fin_boucle
fintantque

Beispiele

Die folgenden Codeausschnitte berechnen alle die Summe der ersten 10 ganzen Zahlen.

            int i, somme, n=10;
            for (i = 1, somme = 0; i <= n; i = i + 1)
                somme = somme + i;

            for (i = 1, somme = 0; i <= n; somme = somme + i, i = i + 1) ;

            i = 1; somme = 0;
            while (i <= n) { somme += i; i++; }

            i = 1; somme = 0;
            do somme += i++;
            while (i <= n);

3.4.4.3. Anweisungen zur Schleifenverwaltung

break
Beenden von loop, while, do ... while.
continue
springt in den Schleifen for, while und do ... while zur nächsten Iteration. while

3.5. Ausnahmebehandlung

Viele C#-Funktionen können Ausnahmen, d. h. Fehler, auslösen. Wenn eine Funktion eine Ausnahme auslösen kann, sollte der Programmierer diese behandeln, um fehlerresistentere Programme zu erhalten: Ein plötzlicher „Absturz“ der Anwendung muss stets vermieden werden.

Eine Ausnahme wird wie folgt behandelt:

try{
    code susceptible de générer une exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Wenn die Funktion keine Ausnahme auslöst, springen wir zur nächsten Anweisung; andernfalls springen wir zum Hauptteil der catch-Klausel und dann zur nächsten Anweisung. Beachten Sie folgende Punkte:

  • e ist ein Objekt vom Typ Exception oder einer davon abgeleiteten Klasse. Sie können präziser vorgehen, indem Sie Typen wie IndexOutOfRangeException, FormatException, SystemException usw. verwenden: Es gibt verschiedene Arten von Ausnahmen. Durch die Schreibweise catch (Exception e) geben Sie an, dass Sie alle Ausnahmetypen behandeln möchten. Wenn der Code im try-Block wahrscheinlich mehrere Arten von Ausnahmen generiert, sollten Sie präziser vorgehen, indem Sie die Ausnahme mit mehreren catch-Anweisungen behandeln:
try{
    code susceptible de générer les exceptions
} catch ( IndexOutOfRangeException e1){
    traiter l'exception e1
}
} catch ( FormatException e2){
    traiter l'exception e2
}
instruction suivante
  • Wir können den try/catch-Klauseln eine finally-Klausel hinzufügen:
try{
    code susceptible de générer une exception
} catch (Exception e){
    traiter l'exception e
}
finally{
    code exécuté après try ou catch
}
instruction suivante

Unabhängig davon, ob eine Ausnahme auftritt oder nicht, wird der Code der finally-Klausel immer ausgeführt.

  • Im catch-Block möchten Sie möglicherweise nicht die verfügbare Exception verwenden. Anstatt catch (Exception e){..} zu schreiben, schreiben wir catch(Exception){...} oder einfach catch {...}.
  • Die Klasse Exception verfügt über eine Eigenschaft Message, die eine Meldung enthält, die den aufgetretenen Fehler detailliert beschreibt. Wenn wir diese also anzeigen möchten, schreiben wir:
catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}",ex.Message);
    ...
}//catch
  • Die Klasse Exception verfügt über eine Methode ToString, die eine Zeichenfolge zurückgibt, die den Typ der Ausnahme und den Wert von Message angibt. Wir können also schreiben:
catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}", ex.ToString());
    ...
}//catch

Wir können auch schreiben:

catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}",ex);
    ...
}//catch

Der Compiler weist dem Parameter {0} den Wert ex.ToString() zu.

Das folgende Beispiel zeigt eine Ausnahme, die durch die Verwendung eines nicht vorhandenen Array-Elements ausgelöst wird:


using System;
 
namespace Chap1 {
    class P08 {
        static void Main(string[] args) {
             // declaring & initializing an array
            int[] tab = { 0, 1, 2, 3 };
            int i;
             // table display with for
            for (i = 0; i < tab.Length; i++)
                Console.WriteLine("tab[{0}]={1}", i, tab[i]);
             // table display with a for each
            foreach (int élmt in tab) {
                Console.WriteLine(élmt);
            }
             // generating an exception
            try {
                tab[100] = 6;
            } catch (Exception e) {
                Console.Error.WriteLine("L'erreur suivante s'est produite : " + e);
                return;
             }//try-catch
            finally {
                Console.WriteLine("finally ...");
            }
        }
    }
}

Oben löst Zeile 18 eine Ausnahme aus, da das Array „tab“ kein Element mit der Nummer 100 enthält. Die Ausführung des Programms liefert folgende Ergebnisse:

tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
0
1
2
3
L'erreur suivante s'est produite : System.IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau.
   à Chap1.P08.Main(String[] args) dans C:\data\travail\2007-2008\c# 2008\poly\Chap1\08\Program.cs:ligne 7
finally ...
  • Zeile 9: Ausnahme [System.IndexOutOfRangeException] aufgetreten
  • Zeile 11: Die finally-Klausel (Zeilen 23–25) des Codes wurde ausgeführt, obwohl Zeile 21 eine return-Anweisung zum Verlassen der Methode enthielt. Beachten Sie, dass die finally-Klausel immer ausgeführt wird.

Hier ist ein weiteres Beispiel dafür, wie man mit der Ausnahme umgeht, die durch die Zuweisung einer Zeichenfolge zu einer Integer-Variablen verursacht wird, wenn die Zeichenfolge keinen Integer darstellt:


using System;
 
namespace Chap1 {
    class P08 {
        static void Main(string[] args) {
 
             // example 2
             // We ask for the name
            Console.Write("Nom : ");
             // reading response
            string nom = Console.ReadLine();
             // age requested
            int age = 0;
            bool ageOK = false;
            while (!ageOK) {
                 // question
                Console.Write("âge : ");
                 // read-verify answer
                try {
                    age = int.Parse(Console.ReadLine());
                    ageOK = age>=1;
                } catch {
                 }//try-catch
                if (!ageOK) {
                    Console.WriteLine("Age incorrect, recommencez...");
                }
             }//while
             // final display
            Console.WriteLine("Vous vous appelez {0} et vous avez {1} an(s)",nom,age);
        }
    }
}
  • Zeilen 15–27: Die Schleife zur Eingabe des Alters einer Person
  • Zeile 20: Die über die Tastatur eingegebene Zeile wird mit int.Parse in eine Ganzzahl umgewandelt. Diese Methode löst eine Ausnahme aus, wenn die Konvertierung nicht möglich ist. Aus diesem Grund wurde die Operation in einen try/catch-Block gesetzt.
  • Zeilen 22–23: Wenn eine Ausnahme ausgelöst wird, springen wir zum Catch-Block, wo nichts geschieht. Somit bleibt die in Zeile 14 auf false gesetzte boolesche Variable ageOK weiterhin false.
  • Zeile 21: Wenn Sie diese Zeile erreichen, war die Konvertierung von String zu int erfolgreich. Überprüfen Sie jedoch, ob die erhaltene Ganzzahl größer oder gleich 1 ist.
  • Zeilen 24–26: Wenn das Alter falsch ist, wird eine Fehlermeldung ausgegeben.

Einige Leistungsergebnisse:

1
2
3
Nom : dupont
âge : 23
Vous vous appelez dupont et vous avez 23 an(s)
1
2
3
4
5
6
7
Nom : durand
âge : x
Age incorrect, recommencez...
âge : -4
Age incorrect, recommencez...
âge : 12
Vous vous appelez durand et vous avez 12 an(s)

3.6. Anwendungsbeispiel – V1

Wir schlagen vor, ein Programm zur Berechnung der Einkommensteuer eines Steuerpflichtigen zu schreiben. Der vereinfachte Fall ist der eines Steuerpflichtigen, der nur sein Gehalt anzugeben hat (Zahlen von 2004 für das Einkommen von 2003):

  • Die Anzahl der Arbeitnehmeranteile wird berechnet als nbParts = nbEnfants/2 + 1, wenn unverheiratet, bzw. nbEnfants/2 + 2, wenn verheiratet, wobei nbEnfants die Anzahl der Kinder ist.
  • Wenn er mindestens drei Kinder hat, erhält er einen halben Anteil mehr
  • Berechnen Sie Ihr zu versteuerndes Einkommen: R = 0,72 × S, wobei S sein Jahresgehalt ist
  • Berechnen Sie Ihren Familienkoeffizienten QF = R / nbParts
  • Berechnen Sie Ihre Steuer I. Betrachten Sie die folgende Tabelle:
4262
0
0
8382
0,0683
291,09
14753
0,1914
1322,92
23888
0,2826
2668,39
38868
0,3738
4846,98
47932
0,4262
6883,66
0
0,4809
9505,54

Jede Zeile enthält 3 Felder. Um die Steuer I zu berechnen, suchen Sie nach der ersten Zeile, in der QF <= champ1 ist. Wenn beispielsweise QF = 5000 ist, finden wir die Zeile

    8382        0.0683        291.09

Steuer I ist dann gleich 0,0683*R - 291,09*nbParts. Wenn QF so ist, dass die Beziehung QF<=champ1 nie geprüft wird, werden die Koeffizienten der letzten Zeile verwendet. Hier:

0                0,4809    9505,54

was zu tax I=0,4809*R - 9505,54*nbParts führt.

Das entsprechende C#-Programm lautet wie folgt:


using System;
 
namespace Chap1 {
    class Impots {
        static void Main(string[] args) {
             // data tables required for tax calculation
            decimal[] limites = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
            decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
            decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };
 
             // marital status is restored
            bool OK = false;
            string reponse = null;
            while (!OK) {
                Console.Write("Etes-vous marié(e) (O/N) ? ");
                reponse = Console.ReadLine().Trim().ToLower();
                if (reponse != "o" && reponse != "n")
                    Console.Error.WriteLine("Réponse incorrecte. Recommencez");
                else OK = true;
             }//while
            bool marie = reponse == "o";
 
             // number of children
            OK = false;
            int nbEnfants = 0;
            while (!OK) {
                Console.Write("Nombre d'enfants : ");
                try {
                    nbEnfants = int.Parse(Console.ReadLine());
                    OK = nbEnfants >= 0;
                } catch {
                 }// try
                if (!OK) {
                    Console.WriteLine("Réponse incorrecte. Recommencez");
                }
             }// while
 
             // salary
            OK = false;
            int salaire = 0;
            while (!OK) {
                Console.Write("Salaire annuel : ");
                try {
                    salaire = int.Parse(Console.ReadLine());
                    OK = salaire >= 0;
                } catch {
                 }// try
                if (!OK) {
                    Console.WriteLine("Réponse incorrecte. Recommencez");
                }
             }// while
 
             // calculating the number of shares
            decimal nbParts;
            if (marie) nbParts = (decimal)nbEnfants / 2 + 2;
            else nbParts = (decimal)nbEnfants / 2 + 1;
            if (nbEnfants >= 3) nbParts += 0.5M;
 
             // taxable income
            decimal revenu = 0.72M * salaire;
 
             // family quotient
            decimal QF = revenu / nbParts;
 
             // search for tax bracket corresponding to QF
            int i;
            int nbTranches = limites.Length;
            limites[nbTranches - 1] = QF;
            i = 0;
            while (QF > limites[i]) i++;
             // tax
            int impots = (int)(coeffR[i] * revenu - coeffN[i] * nbParts);
 
             // the result is displayed
            Console.WriteLine("Impôt à payer : {0} euros", impots);
        }
    }
}
  • Zeilen 7–9: Numerische Werte werden mit dem Suffix M (Money) versehen, um den Typ „decimal“ anzugeben.
  • Zeile 16:
    • Console.ReadLine() sorgt dafür, dass die Zeichenkette C1 typisiert wird
    • C1.Trim() entfernt führende und nachfolgende Leerzeichen C1 – gibt eine Zeichenkette C2 zurück
    • C2.ToLower() erzeugt die Zeichenkette C3, die die in Kleinbuchstaben umgewandelte Zeichenkette C2 ist.
  • Zeile 21: Die boolesche Variable marie erhält den Wert true oder false, je nachdem, ob answer=="o" ist
  • Zeile 29: Die über die Tastatur eingegebene Zeichenkette wird in einen Integer umgewandelt. Wenn die Umwandlung fehlschlägt, wird eine Ausnahme ausgelöst.
  • Zeile 30: Die Boolesche Variable „OK“ erhält den Wert „true“ oder „false“, abhängig davon, ob nbEnfants >= 0 ist
  • Zeilen 55–56: Man kann nicht einfach nbEnfants/2 schreiben. Wenn nbEnfants gleich 3 wäre, hätten wir 3/2, eine ganzzahlige Division, die 1 und nicht 1,5 ergeben würde. Deshalb schreiben wir (decimal)nbEnfants, um einen der Operanden der Division in eine reelle Zahl umzuwandeln und somit eine Division zwischen reellen Zahlen zu erhalten.

Hier sind einige Beispiele:

Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 2
Salaire annuel : 60000
Impôt à payer : 4282 euros
Etes-vous marié(e) (O/N) ? oui
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : trois
Réponse incorrecte. Recommencez
Nombre d'enfants : 3
Salaire annuel : 60000 euros
Réponse incorrecte. Recommencez
Salaire annuel : 60000
Impôt à payer : 2959 euros

3.7. Wichtige Programmargumente

Die Hauptfunktion Main kann ein Array von Zeichenketten als Parameter annehmen: String[] (oder string[]). Diese Tabelle enthält die Befehlszeilenargumente, die zum Starten der Anwendung verwendet werden. Wenn wir beispielsweise das Programm P mit dem folgenden Befehl (DOS) ausführen:


        P arg0 arg1 … argn

und wenn „Main“ wie folgt deklariert ist:

public static void Main(string[] args)

args[0]="arg0", args[1]="arg1" ... Hier ist ein Beispiel:


using System;
 
namespace Chap1 {
    class P10 {
        static void Main(string[] args) {
             // list parameters received
            Console.WriteLine("Il y a  " + args.Length + " arguments");
            for (int i = 0; i < args.Length; i++) {
                Console.Out.WriteLine("arguments[" + i + "]=" + args[i]);
            }
        }
    }
}

Um Argumente an den ausgeführten Code zu übergeben, gehen Sie wie folgt vor:

  • in [1]: Rechtsklick auf das Projekt / Eigenschaften
  • in [2]: Registerkarte [Debug]
  • in [3]: Argumente eingeben

Die Ausführung liefert folgende Ergebnisse:

1
2
3
4
5
Il y a  4 arguments
arguments[0]=a0
arguments[1]=a1
arguments[2]=a2
arguments[3]=a3

Beachten Sie, dass die

public static void Main()

gültig ist, wenn Main keine Parameter erwartet.

3.8. Aufzählungen

Eine Aufzählung ist ein Datentyp, dessen Wertebereich aus einer Menge von ganzzahligen Konstanten besteht. Betrachten wir ein Programm, das die Noten einer Prüfung verwalten soll. Es gäbe fünf davon: Befriedigend, Gut, Sehr gut, Ausgezeichnet.

Wir könnten dann eine Aufzählung für diese fünf Konstanten definieren:


        enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };

Intern werden diese fünf Konstanten durch aufeinanderfolgende Ganzzahlen kodiert, beginnend mit 0 für die erste Konstante, 1 für die nächste usw. Eine Variable kann so deklariert werden, dass sie diese Werte aus der Aufzählung annimmt:


            // une variable qui prend ses valeurs dans l'énumération Mentions
Mentions maMention = Mentions.Passable;

Eine Variable kann mit den verschiedenen möglichen Werten der Aufzählung verglichen werden:


            if (maMention == Mentions.Passable) {
                Console.WriteLine("Peut mieux faire");
}

Sie können alle Werte der Aufzählung abrufen:


             // list of statements in string form
            foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
                Console.WriteLine(m);
}

Genauso wie der einfache Typ int dem System.Int32 entspricht, entspricht der einfache Typ enum dem System.Enum. Diese Struktur verfügt über eine statische Methode GetValues, mit der Sie alle Werte eines Aufzählungstyps abrufen können, der als Parameter übergeben wird. Dabei muss es sich um ein Objekt vom Typ Type handeln, einer Klasse, die Informationen über den Datentyp enthält. Der Typ einer Variablen v wird mit v.GetType() ermittelt. Der Typ von T wird mit typeof(T) ermittelt. Hier liefert also maMention.GetType() das Objekt Type der Aufzählung Mentions und Enum.GetValues(maMention.GetType()) die Liste der Aufzählungswerte von Mentions.

Wenn wir nun schreiben


             //list of mentions in integer form
            foreach (int m in Enum.GetValues(typeof(Mentions))) {
                Console.WriteLine(m);
}

Zeile 2: Die Schleifenvariable ist vom Typ Integer. Anschließend erhalten wir die Liste der Aufzählungswerte in ganzzahliger Form. Das Objekt vom Typ System.Type, das dem Datentyp Mentions entspricht, wird über typeof(Mentions) abgerufen. Wir hätten auch maMention.GetType() schreiben können.

Das folgende Programm veranschaulicht das eben Geschriebene:


using System;
 
namespace Chap1 {
    class P11 {
        enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };
        static void Main(string[] args) {
             // a variable that takes its values from the Mentions enumeration
            Mentions maMention = Mentions.Passable;
             // variable value display
            Console.WriteLine("mention=" + maMention);
             // test with enumeration value
            if (maMention == Mentions.Passable) {
                Console.WriteLine("Peut mieux faire");
            }
             // list of statements in string form
            foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
                Console.WriteLine(m);
            }
             //list of mentions in integer form
            foreach (int m in Enum.GetValues(typeof(Mentions))) {
                Console.WriteLine(m);
            }
        }
    }
}

Die Ergebnisse lauten wie folgt:

mention=Passable
Peut mieux faire
Passable
AssezBien
Bien
TrèsBien
Excellent
0
1
2
3
4

3.9. Parameter an eine Funktion übergeben

Hier interessiert uns, wie Parameter an eine Funktion übergeben werden. Betrachten Sie die folgende statische Funktion:


        private static void ChangeInt(int a) {
            a = 30;
            Console.WriteLine("Paramètre formel a=" + a);
}

In der Funktionsdefinition, Zeile 1, wird a als formaler Parameter bezeichnet. Er dient ausschließlich der Definition von changeInt. Er hätte genauso gut b heißen können. Betrachten wir nun eine Anwendung dieser Funktion:


        public static void Main() {
            int age = 20;
            ChangeInt(age);
            Console.WriteLine("Paramètre effectif age=" + age);
}

Hier in der Anweisung in Zeile 3, ChangeInt(age), ist age der effektive Parameter, der seinen Wert an den formalen Parameter a übergibt. Uns interessiert, wie ein formaler Parameter den Wert eines effektiven Parameters abruft.

3.9.1. Übergabe per Wert

Das folgende Beispiel zeigt, dass Funktionsparameter standardmäßig per Wert übergeben werden, d. h. der Wert des tatsächlichen Parameters wird in den entsprechenden formalen Parameter kopiert. Wir haben zwei unterschiedliche Entitäten. Wenn die Funktion den formalen Parameter ändert, bleibt der effektive Parameter unverändert.


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
            int age = 20;
            ChangeInt(age);
            Console.WriteLine("Paramètre effectif age=" + age);
        }
        private static void ChangeInt(int a) {
            a = 30;
            Console.WriteLine("Paramètre formel a=" + a);
        }
    }
}

Die Ergebnisse lauten wie folgt:

Paramètre formel a=30
Paramètre effectif age=20

Der Wert 20 des effektiven Parameters age wurde in den formalen Parameter a kopiert (Zeile 10). Dieser wurde anschließend geändert (Zeile 11). Der tatsächliche Parameter blieb unverändert. Dieser Modus eignet sich für Funktionseingabeparameter.

3.9.2. Übergabe per Referenz

Bei einer Referenzübergabe sind der effektive Parameter und der formale Parameter ein und dieselbe Entität. Wenn die Funktion den formalen Parameter ändert, wird auch der effektive Parameter geändert. In C# muss beiden das Schlüsselwort ref vorangestellt werden:

Hier ein Beispiel:


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 2
            int age2 = 20;
            ChangeInt2(ref age2);
            Console.WriteLine("Paramètre effectif age2=" + age2);
        }
        private static void ChangeInt2(ref int a2) {
            a2 = 30;
            Console.WriteLine("Paramètre formel a2=" + a2);
        }
    }
}

und Leistungsergebnisse:

Paramètre formel a2=30
Paramètre effectif age2=30

Der effektive Parameter hat die Änderung des formalen Parameters übernommen. Dieser Modus eignet sich für Funktionsausgabeparameter.

3.9.3. Übergabe per Referenz mit dem Schlüsselwort „out“

Betrachten wir das vorherige Beispiel, in dem die Variable age2 vor dem Aufruf von changeInt nicht initialisiert worden wäre:


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 2
            int age2;
            ChangeInt2(ref age2);
            Console.WriteLine("Paramètre effectif age2=" + age2);
        }
        private static void ChangeInt2(ref int a2) {
            a2 = 30;
            Console.WriteLine("Paramètre formel a2=" + a2);
        }
    }
}

Wenn wir dieses Programm kompilieren, erhalten wir folgende Fehlermeldung:

    Use of unassigned local variable 'age2'

Wir können dieses Problem umgehen, indem wir age2 einen Anfangswert zuweisen. Sie können das Schlüsselwort ref auch durch das Schlüsselwort out ersetzen. Damit drücken wir aus, dass der Parameter nur ein Ausgabeparameter ist und daher keinen Anfangswert benötigt:


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 3
            int age3;
            ChangeInt3(out age3);
            Console.WriteLine("Paramètre effectif age3=" + age3);
        }
        private static void ChangeInt3(out int a3) {
            a3 = 30;
            Console.WriteLine("Paramètre formel a3=" + a3);
        }
    }
}

Die Ergebnisse lauten wie folgt:

Paramètre formel a3=30
Paramètre effectif age3=30