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:
- Ganzzahlen
- Reelle Zahlen
- Dezimalzahlen
- Zeichen und Zeichenfolgen
- Boolesche Werte
- Objekte
3.2.1. Vordefinierte Datentypen
C#-Typ | Typ .NET | Dargestellte Daten | Suffix Literalwerte | Codierung | Wertebereich |
Zeichen (S) | Zeichen | 2 Bytes | Unicode-Zeichen (UTF-16) | ||
Zeichenkette (C) | Zeichenkette | Verweis auf eine Folge von Unicode-Zeichen | |||
Int32 (S) | ganze Zahl | 4 Bytes | [-2³¹; 2³¹-1] [-2147483648; 2147483647] | ||
UInt32 (S) | .. | U | 4 Bytes | [0, 2³²-1] [0, 4294967295] | |
Int64 (S) | .. | L | 8 Bytes | [-263, 263 -1] [-9223372036854775808, 9223372036854775807] | |
UInt64 (S) | .. | UL | 8 Bytes | [0, 264 -1] [0, 18446744073709551615] | |
.. | 1 Byte | [-27, 27 -1] [-128, +127] | |||
Byte (S) | .. | 1 Byte | [0 , 28 -1] [0,255] | ||
Int16 (S) | .. | 2 Bytes | [-215, 215-1] [-32768, 32767] | ||
UInt16 (S) | .. | 2 Bytes | [0, 216-1] [0,65535] | ||
Single (S) | reelle Zahl | F | 4 Bytes | [1,5 × 10⁻⁴⁵, 3,4 × 10³⁸ in absoluten Werten | |
Double (S) | .. | D | 8 Byte | [-1,7 × 10³⁰⁸, 1,7 × 10³⁰⁸ in absoluten Werten | |
Dezimal (S) | Dezimalzahl | M | 16 Bytes | [1,0 × 10⁻²⁸, 7,9 × 10²⁸] im Absolutwert mit 28 signifikanten Stellen | |
Boolesche (S) | .. | 1 Byte | wahr, falsch | ||
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:
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:
Die Anzeige gibt .NET-Typen wieder, keine C#-Aliase.
3.2.2. Notation von Literalwerten
145, -7, 0xFF (hexadezimal) | |
100000L | |
134,789, -45E-18 (-45 × 10⁻¹⁸) | |
134,789F, -45E-18F (-45 × 10⁻¹⁸) | |
100000M | |
'A', 'b' | |
"today" "c:\Kap1\Absatz3" @"c:\Kap1\Absatz3" | |
true, false | |
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:
Beispiel:
Warum Konstanten deklarieren?
- Das Programm ist leichter zu lesen, wenn die Konstante einen aussagekräftigen Namen erhält:
- 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:
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:
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:
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:
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
nombre.ToString() | |
int.Parse(string) oder System.Int32.Parse | |
long.Parse(string) oder System.Int64.Parse | |
double.Parse(string) oder System.Double.Parse(string) | |
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:
Beachten Sie, dass bei reellen Zahlen in Zeichenfolgenform ein Komma und kein Dezimalpunkt verwendet werden muss. Daher schreiben wir
aber
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:
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:
oder einfach:
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:
oder einfach:
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:
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.
![]() |
-
Einlesen von Informationen von der Tastatur
-
Verarbeitung von Informationen
-
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:
Die Klasse „Console“ bietet eine Methode „ReadLine“, die standardmäßig mit „In“ verknüpft ist. Wir können daher schreiben:
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:
- 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:
- Standard-Eingabegerät – standardmäßig die Tastatur und mit der Nummer 0
- Standardausgabegerät – standardmäßig der Bildschirm, Nummer 1
- 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:
Hinter den Argumenten des Programms „argi pg“ können Standard-E/A-Geräte in Dateien umgeleitet werden:
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. | |
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 | |
ebenso, aber die geschriebenen Daten werden an den aktuellen Inhalt der Datei „out.txt“ angehängt. | |
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 | |
dito, aber die geschriebenen Daten werden an den aktuellen Dateiinhalt von error.txt angehängt. | |
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:
- 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
zulässig ist. Aufgrund der Auswertungsreihenfolge wird der =-Operator ganz rechts ausgewertet. Wir erhalten daher
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:
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:
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:
Quadratwurzel | |
Cosinus | |
Sinus | |
Tangens | |
x hoch y (x > 0) | |
Exponential | |
Neper-Logarithmus | |
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:
Die vollständige Definition von Math lautet wie folgt:





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):
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“.
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:
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
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 > Kanal 2
-1 wenn Kette Nr. 1 < 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:
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:
- !
- &&
- ||
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.
verschiebt i um n Bits nach links. Die eingehenden Bits sind Nullen. | |
verschiebt i um n Bits nach rechts. Wenn i eine vorzeichenbehaftete Ganzzahl ist (signed char, int, long), bleibt das Vorzeichenbit erhalten. | |
führt die logische UND-Verknüpfung von i und j Bit für Bit durch. | |
führt die logische ODER-Verknüpfung von i und j bitweise durch. | |
komplementiert i zu 1 | |
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:
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
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
gd | |
dg | |
dg | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
dg | |
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:
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:
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.
beendet die Programmausführung mit einem Statuswert von 0.
3.4.2. Einfache Entscheidungsstruktur
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:
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#
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:
was wie folgt übersetzt werden kann:
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:
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 :
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 :
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:
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
Beenden von loop, while, do ... while. | |
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:
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:
- 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:
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
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) ? 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:
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:
Beachten Sie, dass die
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:
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:
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:
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:
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:





