Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 2 Grundlagen der Sprache C#
Pfeil 2.1 Konsolenanwendungen
Pfeil 2.1.1 Allgemeine Anmerkungen
Pfeil 2.1.2 Ein erstes Konsolenprogramm
Pfeil 2.2 Grundlagen der C#-Syntax
Pfeil 2.2.1 Kennzeichnen, dass eine Anweisung abgeschlossen ist
Pfeil 2.2.2 Anweisungs- und Gliederungsblöcke
Pfeil 2.2.3 Kommentare
Pfeil 2.2.4 Die Groß- und Kleinschreibung
Pfeil 2.2.5 Die Struktur einer Konsolenanwendung
Pfeil 2.3 Variablen und Datentypen
Pfeil 2.3.1 Variablendeklaration
Pfeil 2.3.2 Der Variablenbezeichner
Pfeil 2.3.3 Der Zugriff auf eine Variable
Pfeil 2.3.4 Ein- und Ausgabemethoden der Klasse »Console«
Pfeil 2.3.5 Die einfachen Datentypen
Pfeil 2.3.6 Typkonvertierung
Pfeil 2.4 Operatoren
Pfeil 2.4.1 Arithmetische Operatoren
Pfeil 2.4.2 Vergleichsoperatoren
Pfeil 2.4.3 Logische Operatoren
Pfeil 2.4.4 Bitweise Operatoren
Pfeil 2.4.5 Zuweisungsoperatoren
Pfeil 2.4.6 Stringverkettung
Pfeil 2.4.7 Sonstige Operatoren
Pfeil 2.4.8 Operator-Vorrangregeln
Pfeil 2.5 Datenfelder (Arrays)
Pfeil 2.5.1 Die Deklaration und Initialisierung eines Arrays
Pfeil 2.5.2 Der Zugriff auf die Array-Elemente
Pfeil 2.5.3 Mehrdimensionale Arrays
Pfeil 2.5.4 Festlegen der Array-Größe zur Laufzeit
Pfeil 2.5.5 Bestimmung der Array-Obergrenze
Pfeil 2.5.6 Die Gesamtanzahl der Array-Elemente
Pfeil 2.5.7 Verzweigte Arrays
Pfeil 2.6 Kontrollstrukturen
Pfeil 2.6.1 Die »if«-Anweisung
Pfeil 2.6.2 Das »switch«-Statement
Pfeil 2.7 Programmschleifen
Pfeil 2.7.1 Die »for«-Schleife
Pfeil 2.7.2 Die »foreach«-Schleife
Pfeil 2.7.3 Die »do«- und die »while«-Schleife

Rheinwerk Computing - Zum Seitenanfang

2.3 Variablen und DatentypenZur nächsten Überschrift

Dateninformationen bilden die Grundlage der Datenverarbeitung und hauchen einem Programm Leben ein: Daten können anwendungsspezifisch sein, den Zustand von Objekten beschreiben, Informationen aus Datenbanken repräsentieren oder auch nur eine Netzwerkadresse. Daten bilden also gemeinhin die Basis der Gesamtfunktionalität einer Anwendung.


Rheinwerk Computing - Zum Seitenanfang

2.3.1 VariablendeklarationZur nächsten ÜberschriftZur vorigen Überschrift

Praktisch jedes Programm benötigt Daten, um bestimmte Aufgaben zu erfüllen. Daten werden in Variablen vorgehalten. Dabei steht eine Variable für eine Adresse im Hauptspeicher des Rechners. Ausgehend von dieser Adresse wird eine bestimmte Anzahl von Bytes reserviert – entsprechend dem Typ der Variablen. Das, was eine Variable repräsentiert, kann vielfältiger Art sein: eine einfache Ganzzahl, eine Fließkommazahl, ein einzelnes Zeichen, eine Zeichenkette, eine Datums- oder Zeitangabe, aber auch die Referenz auf die Startadresse eines Objekts.

Der Bezeichner einer Variablen dient dazu, die Speicheradresse des Werts im Programmcode mit einem Namen anzusprechen, der sich einfach merken lässt. Er ist also vom Wesen her nichts anderes als ein Synonym oder Platzhalter eines bestimmten Speicherorts.

Variablen müssen deklariert werden. Unter einer Deklaration wird die Bekanntgabe des Namens der Variablen sowie des von ihr repräsentierten Datentyps verstanden. Die Deklaration muss vor der ersten Wertzuweisung an die Variable erfolgen. Dabei wird zuerst der Datentyp angegeben, dahinter der Variablenname. Abgeschlossen wird die Deklaration mit einem Semikolon. Damit lautet die allgemeine Syntax:

Datentyp Bezeichner;

Beispielsweise könnte eine zulässige Deklaration wie folgt aussehen:

int value;

Damit wird dem Compiler mitgeteilt, dass der Bezeichner value für einen Wert steht, der vom Typ einer Ganzzahl, genauer gesagt vom Typ int (Integer) ist. Mit

value = 1000;

wird dieser Variablen ein gültiger Wert zugewiesen. Man spricht dann auch von der Initialisierung der Variablen.

Variablen, die innerhalb einer Methode wie beispielsweise Main deklariert sind, gelten noch nicht als initialisiert. Sie enthalten keinen gültigen Wert, auch nicht die Zahl 0. Daher kann ihr Inhalt auch nicht ausgewertet werden.

Wenn Sie versuchen, auf eine nicht deklarierte Variable zuzugreifen, wird der C#-Compiler einen Fehler melden. Ebenso falsch ist es, den Inhalt einer nicht initialisierten Variablen auswerten zu wollen.

Deklaration und Initialisierung können auch in einer einzigen Anweisung erfolgen:

int value = 0;

Auf diese Weise vermeiden Sie zumindest eine nicht initialisierte Variable. Müssen Sie mehrere Variablen gleichen Typs deklarieren, können Sie die Bezeichner, getrennt durch ein Komma, hintereinander angeben:

int a, b, c;

Sie können dann auch eine oder mehrere Variablen sofort initialisieren:

int a, b = 9, c = 12;

Rheinwerk Computing - Zum Seitenanfang

2.3.2 Der VariablenbezeichnerZur nächsten ÜberschriftZur vorigen Überschrift

Ein Variablenname unterliegt besonderen Reglementierungen:

  • Ein Bezeichner darf sich nur aus alphanumerischen Zeichen und dem Unterstrich zusammensetzen. Leerzeichen und andere Sonderzeichen wie beispielsweise #, §, $ usw. sind nicht zugelassen.
  • Ein Bezeichner muss mit einem Buchstaben oder dem Unterstrich anfangen.
  • Ein einzelner Unterstrich als Variablenname ist nicht zulässig.
  • Der Bezeichner muss eindeutig sein. Er darf nicht gleichlautend mit einem Schlüsselwort, einer Prozedur, einer Klasse oder einem Objektnamen sein.

Zur Verdeutlichung dieser Regeln folgen hier einige Beispiele für korrekte und falsche Variablenbezeichner:

// korrekte Variablenbezeichner
long value;
byte value_12;
int VaLuE;
// fehlerhafte Variablenbezeichner
int 34value;
string message Text;
long value%long;

Noch ein Hinweis zur Namensvergabe: Wählen Sie grundsätzlich beschreibende Namen, damit Ihr Code später besser lesbar wird. Einfache Bezeichner wie x oder y usw. sind wenig aussagekräftig. Besser wäre eine Wahl wie farbe, gehalt, vorname usw. Nur den Zählervariablen von Schleifen werden meistens Kurznamen gegeben.

Die hier exemplarisch angegebenen Variablenbezeichner fangen alle mit einem Kleinbuchstaben an. Folgen Sie der allgemeinen .NET-Namenskonvention, sollten alle Variablen, die in einer Methode definiert sind, mit einem Kleinbuchstaben beginnen. Man spricht bei diesen Variablen auch von »lokalen Variablen«. Alle anderen Fälle jetzt aufzuführen, würde den Rahmen momentan sprengen. Es sei aber angemerkt, dass in diesem Buch die Variablenbezeichner fast durchweg der .NET-Namenskonvention folgen.


Rheinwerk Computing - Zum Seitenanfang

2.3.3 Der Zugriff auf eine VariableZur nächsten ÜberschriftZur vorigen Überschrift

Wir wollen uns jetzt noch ansehen, wie wir uns den Inhalt einer Variablen an der Konsole ausgeben lassen können. Wir deklarieren dazu eine Variable vom Typ long und weisen ihr einen Wert zu, den wir danach an der Konsole ausgeben lassen.

static void Main(string[] args)
{
long value = 4711;
Console.WriteLine("value = {0}", value);
Console.ReadLine();
}

Deklaration und Initialisierung bieten keine Neuigkeiten, im Gegensatz zu der Anweisung, die eine Ausgabe an der Konsole bewirkt:

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

Die Ausgabe im Befehlsfenster wird wie folgt lauten:

value = 4711

Sie haben bereits gesehen, dass mit Console.WriteLine eine einfache Konsolenausgabe codiert wird. WriteLine ist eine Methode, die in der Klasse Console definiert ist. Jetzt fehlt noch die genaue Erklärung der verwendeten Syntax.


Rheinwerk Computing - Zum Seitenanfang

2.3.4 Ein- und Ausgabemethoden der Klasse »Console«Zur nächsten ÜberschriftZur vorigen Überschrift

Es bleibt uns nichts anderes übrig, als an dieser Stelle schon einen kleinen Ausflug in die Welt der Klassen und Objekte zu unternehmen, weil wir immer wieder mit den Methoden verschiedener Klassen arbeiten werden. Es handelt sich dabei meist um Methoden, um an der Eingabekonsole Ein- und Ausgabeoperationen durchzuführen: Write und WriteLine sowie Read und ReadLine.

Die Methoden »WriteLine«, »ReadLine«, »Write« und »Read«

Die Klasse Console ermöglicht es, über die beiden Methoden Write und WriteLine auf die Standardausgabeschnittstelle zuzugreifen. Der Begriff »Ausgabeschnittstelle« mag im ersten Moment ein wenig verwirren, aber tatsächlich wird darunter die Anzeige an der Konsole verstanden.

WriteLine und Write unterscheiden sich dahingehend, dass die erstgenannte Methode dem Ausgabestring automatisch einen Zeilenumbruch anhängt und den Cursor in die folgende Ausgabezeile setzt. Nach dem Aufruf der Methode Write verbleibt der Eingabecursor weiterhin in der aktuellen Ausgabezeile. Beide Methoden sind aber ansonsten auf identische Weise einsetzbar.

Grundsätzlich gilt: Wollen wir die Methode eines Objekts oder einer Klasse aufrufen, geben wir den Objekt- bzw. Klassennamen an und von diesem durch einen Punkt getrennt den Namen der Methode. Man spricht hierbei auch von der sogenannten Punktnotation. Hinter dem Methodennamen schließt sich ein Klammerpaar an. Allgemein lautet die Syntax also:

Objektname.Methodenname();

Sie können sich mit dieser Syntax durchaus schon vertraut machen, denn sie wird Ihnen ab sofort überall begegnen, da sie in objektorientiertem Programmcode elementar ist.

Das runde Klammerpaar hinter der Read- bzw. ReadLine-Methode bleibt immer leer. Bei den Methoden Write und WriteLine werden innerhalb der Klammern die auszugebenden Daten einschließlich ihres Ausgabeformats beschrieben. Allerdings dürfen auch bei den beiden letztgenannten Methoden die Klammern leer bleiben.

Im einfachsten Fall wird einer der beiden Ausgabemethoden eine Zeichenfolge in Anführungsstrichen übergeben:

Console.WriteLine("C# macht Spaß.");

Formatausdrücke in den Methoden »Write« und »WriteLine«

Damit sind die Möglichkeiten der Write/WriteLine-Methoden noch lange nicht erschöpft. Die flexiblen Formatierungsmöglichkeiten erlauben die Ausgabe von Daten an beliebigen Positionen innerhalb der Ausgabezeichenfolge. Dazu dient ein Platzhalter, der auch als Formatausdruck bezeichnet wird. Dieser ist an den geschweiften Klammern zu erkennen und enthält zumindest eine Zahl. Hinter der auszugebenden Zeichenfolge werden, durch ein Komma getrennt, die Informationen übergeben, was anstelle des Formatausdrucks auszugeben ist. Sehen wir uns dazu ein Beispiel an:

string text1 = "C#";
string text2 = "Spass";
Console.Write("{0} macht {1}.", text1, text2);

Listing 2.7 Formatausdruck in der Methode »Console.WriteLine«

Hier sind die beiden Variablen text1 und text2 vom Typ string deklariert, die mit einer in Anführungsstrichen gesetzten Zeichenfolge initialisiert werden.

Die auszugebende Zeichenfolge wird in Anführungsstriche gesetzt. Getrennt durch Kommata werden dahinter die beiden Variablen text1 und text2 bekannt gegeben. Der Inhalt der zuerst genannten Variablen text1 ersetzt den Formatausdruck {0} innerhalb der Ausgabezeichenfolge, die zweite Variable text2 ersetzt den Formatausdruck {1}. Entscheidend ist, dass dem ersten Parameter (text1) die Zahl 0 zugeordnet wird, dem zweiten (text2) die Zahl 1 usw. Die Konsolenausgabe lautet:

C# macht Spaß.

Innerhalb des Ausgabestrings müssen die anzuzeigenden Listenelemente nicht der Reihenfolge nach durchlaufen werden. Man kann sie beliebig ansprechen oder sogar einfach ungenutzt lassen. Die Anweisung

Console.Write("{1} macht {0}.", text1, text2);

würde demnach zu der folgenden Ausgabe führen:

Spaß macht C#.

Der Formatausdruck {} dient nicht nur der eindeutigen Bestimmung des Elements, er ermöglicht auch eine weitergehende Einflussnahme auf die Ausgabe. Soll der einzusetzende Wert eine bestimmte Breite einnehmen, gilt die syntaktische Variante:

{N, M}

Dabei gilt Folgendes:

  • N ist ein nullbasierter Zähler.
  • M gibt die Breite der Ausgabe an.

Unbesetzte Plätze werden durch eine entsprechende Anzahl von Leerzeichen aufgefüllt. Sehen wir uns dazu ein Codefragment an:

int value = 10;
Console.WriteLine("Ich kaufe {0,3} Eier", value);
Console.WriteLine("Ich kaufe {0,10} Eier", value);

Listing 2.8 Erweiterte Formatierungsmöglichkeiten

Die Ausgabe des Listings 2.8 lautet hier:

Ich kaufe  10 Eier
Ich kaufe 10 Eier

Die erste Ausgabe hat eine Gesamtbreite von drei Zeichen, die Zahl selbst ist allerdings nur zwei Ziffern breit. Daher wird vor der Zahl ein Leerzeichen gesetzt. Da für die Breite der zweiten Ausgabe zehn Zeichen vorgeschrieben sind, werden links von der Zahl acht Leerstellen eingefügt.

Die Breite darf auch eine negative Zahl sein. Die Ausgabe erfolgt dann linksbündig, daran schließen sich die Leerstellen an.

Sie können den Formatausdruck so spezifizieren, dass numerische Ausgabedaten eine bestimmte Formatierung annehmen. Das führt uns zu der vollständigen Syntax des Formatausdrucks:

// Syntax des Formatausdrucks
{N [,M ][: Format]}

Format spezifiziert, wie die Daten angezeigt werden. In Tabelle 2.1 werden die möglichen Optionen aufgelistet.

Tabelle 2.1 Formatangaben der Formatausgabe

Formatangabe Beschreibung

C

Zeigt die Zahl im lokalen Währungsformat an.

D

Zeigt die Zahl als dezimalen Integer an.

E

Zeigt die Zahl im wissenschaftlichen Format an (Exponentialschreibweise).

F

Zeigt die Zahl im Festpunktformat an.

G

Eine numerische Zahl wird entweder im Festpunkt- oder im wissenschaftlichen Format angezeigt. Zur Anzeige kommt das »kompakteste« Format.

N

Zeigt eine numerische Zahl einschließlich Kommaseparatoren an.

P

Zeigt die numerische Zahl als Prozentzahl an.

X

Die Anzeige erfolgt in Hexadezimalnotation.

An alle Formatangaben kann eine Zahl angehängt werden, aus der die Anzahl der signifikanten Stellen hervorgeht. Nachfolgend sollen einige Beispiele den Einsatz der Formatangaben demonstrieren:

int value = 4711;
// Ausgabe: value=4,711000E+003
Console.WriteLine("value={0:E}", value);
// Ausgabe: value=4,71E+003
Console.WriteLine("value={0:E2}", value);
int value = 225;
// Ausgabe: value=E1
Console.WriteLine("value={0:X}", value);
float value = 0.2512F;
// Ausgabe: value= 0,2512
Console.WriteLine("value={0,10:G}", value);
// Ausgabe: value=25,1200%
Console.WriteLine("value={0:P4}", value);

Listing 2.9 Verschieden formatierte Ausgaben

Escape-Zeichen

Ähnlich wie andere Hochsprachen stellt C# eine Reihe von Escape-Sequenzen zur Verfügung, die dann verwendet werden, wenn Sonderzeichen innerhalb einer Zeichenfolge ausgegeben werden sollen. Beispielsweise kann man mit dem Zeichen \n einen Zeilenumbruch erzwingen:

Console.Write("C#\nmacht\nSpaß.");

An der Konsole wird dann

C#
macht
Spaß.

angezeigt.

Tabelle 2.2 Die Escape-Zeichen

Escape-Zeichen Beschreibung

\'

Fügt ein Hochkomma in die Zeichenfolge ein.

\''

Fügt Anführungsstriche ein.

\\

Fügt einen Backslash in die Zeichenfolge ein.

\a

Löst einen Alarmton aus.

\b

Führt zum Löschen des vorhergehenden Zeichens.

\f

Löst einen Formularvorschub bei Druckern aus.

\n

Löst einen Zeilenvorschub aus (entspricht der Funktionalität der Enter-Taste).

\r

Führt zu einem Wagenrücklauf.

\t

Führt auf dem Bildschirm zu einem Tabulatorsprung.

\u

Fügt ein Unicode-Zeichen in die Zeichenfolge ein.

\v

Fügt einen vertikalen Tabulator in eine Zeichenfolge ein.

Mit Escape-Sequenzen lässt sich die Ausgabe von Sonderzeichen sicherstellen. Es ist aber auch vorstellbar, dass Zeichen, die vom Compiler als Escape-Sequenz interpretiert werden, selbst Bestandteil der Zeichenfolge sind. Fügen Sie dazu nur noch einen weiteren Schrägstrich ein. Dazu ein kleines Beispiel. Angenommen, Sie möchten die Ausgabe

Hallo\nWelt

erzwingen. Sie müssten dann die folgende Anweisung codieren:

Console.WriteLine("Hallo\\nWelt");

Um die Interpretation als Escape-Sequenz für eine gegebene Zeichenfolge vollständig abzuschalten, wird vor der Zeichenfolge das Zeichen »@« gesetzt.

Console.Write(@"C#\nmacht\nSpaß.");

Jetzt lautet die Konsolenausgabe:

C#\nmacht\nSpaß.

Die Methoden »ReadLine« und »Read«

Die Methode ReadLine liest ein oder mehrere Zeichen aus dem Eingabestrom – in unserem Fall ist das die Tastatur. Die Bereitschaft der Methode, auf Zeichen zu warten, endet mit dem Zeilenumbruch, der jedoch selbst nicht zu den eingelesenen Daten gehört. Die eingelesene Zeichenfolge wird von der Methode als Zeichenfolge vom Typ string zurückgeliefert und kann einer string-Variablen zugewiesen werden.

string input = Console.ReadLine();
Console.WriteLine(input);

Wir haben bisher die ReadLine-Methode dazu benutzt, um die Konsole bis zum Drücken der Enter-Taste geöffnet zu halten. In diesem Fall war der Eingabestrom immer leer, der Rückgabewert wurde ignoriert und landete im Nirwana.

Werfen wir nun einen Blick auf die Read-Methode. Diese nimmt nur ein Zeichen aus dem Eingabestrom und gibt dessen ASCII-Wert zurück. Der Rückgabewert von Read ist daher keine Zeichenfolge, sondern eine Zahl vom Typ int.

Es gibt aber noch einen weiteren, nicht weniger wichtigen Unterschied zwischen Read und ReadLine: Die ReadLine-Methode liest eine ganze Zeile und benutzt den Zeilenumbruch dazu, das Ende der Eingabe zu erkennen. Danach wird der Zeilenumbruch dem Eingabestrom entnommen und gelöscht. Die Read-Methode arbeitet anders, denn der Zeilenumbruch wird nicht aus dem Eingabestrom geholt, sondern verbleibt dort und wird so lange gepuffert, bis er von einer anderen Anweisung gelöscht wird. Das kann wiederum nur die Methode ReadLine sein. Schauen Sie sich dazu das folgende Listing an:

static void Main(string[] args)
{
int input = Console.Read();
Console.WriteLine(input);
Console.ReadLine();
}

Listing 2.10 Ein Zeichen mit »Console.Read« einlesen

Nach dem Start des Programms wartet Read auf die Eingabe des Anwenders und erkennt am Zeilenumbruch das Eingabeende. Der Zeilenumbruch befindet sich weiterhin im Eingabestrom und harrt geduldig der kommenden Anweisungen. Die Anweisung in der letzten Zeile, die ReadLine-Methode, reagiert als Erstes wieder auf den Eingabestrom, erkennt darin den Zeilenumbruch und verarbeitet ihn. Das ist gleichzeitig auch das Signal, mit der nächsten Anweisung fortzufahren. Da aber das Ende der Main-Methode erreicht ist, schließt sich das Konsolenfenster sofort. Erst ein zweiter Aufruf von ReadLine würde den eigentlich angedachten Zweck erfüllen, nämlich das Fenster geöffnet zu halten und die Ausgabe der WriteLine-Methode auf unbestimmte Zeit anzuzeigen.


Rheinwerk Computing - Zum Seitenanfang

2.3.5 Die einfachen DatentypenZur nächsten ÜberschriftZur vorigen Überschrift

Die .NET-Laufzeitumgebung verfolgt das Konzept der Objektorientierung nach strengen Maßstäben. Selbst einfache Datentypen werden als Objekte angesehen, die Methoden bereitstellen, um mit einer Variablen bestimmte Aktionen auszuführen. In Tabelle 2.3 sind alle Datentypen von C# zusammenfassend aufgeführt, die allgemein als elementare Datentypen oder manchmal auch als primitive Datentypen bezeichnet werden.

Tabelle 2.3 Die elementaren Datentypen

.NET-Laufzeittyp C# -Alias CLS-konform Wertebereich

Byte

byte

ja

0 ... 255

SByte

sbyte

nein

–128 ... 127

Int16

short

ja

–215 ... 215 –1

UInt16

ushort

nein

0 ... 65535

Int32t

int

ja

–231 ... 231 –1

UInt32

uint

nein

0 ... 232 –1

Int64

long

ja

–263 ... 263 –1

UInt64

ulong

nein

0 ... 264 –1

Single

float

ja

1,4 * 10–45 bis 3,4 * 1038

Double

double

ja

5,0 * 10–324 bis 1,7 * 10308

Decimal

decimal

ja

+/–79E27 ohne Dezimalpunktangabe;
+/–7.9E-29, falls 28 Stellen hinter dem Dezimalpunkt angegeben werden. Die kleinste darstellbare Zahl beträgt +/–1.0E-29.

Char

char

ja

Unicode-Zeichen zwischen 0 und 65535

String

string

ja

ca. 231 Unicode-Zeichen

Boolean

bool

ja

true oder false

Object

object

ja

Eine Variable vom Typ Object kann jeden anderen Datentyp enthalten, ist also universell.

In der ersten Spalte ist der Typbezeichner in der .NET-Klassenbibliothek angeführt. In der zweiten Spalte steht der C#-Alias, der bei der Deklaration einer Variablen dieses Typs angegeben werden kann.

In der dritten Spalte ist angegeben, ob der Typ den Vorgaben der Common Language Specification (CLS) entspricht. Das ist, wie Sie sehen können, nicht bei allen Datentypen der Fall. Doch welche Konsequenzen hat das für Sie und Ihr Programm? Wie ich bereits in Kapitel 1 erwähnt habe, steht C# nur an der Spitze vieler .NET-spezifischen Programmiersprachen. Alle müssen der CLS entsprechen, das ist die Spielregel. Für die in Tabelle 2.3 aufgeführten nicht-CLS-konformen Datentypen bedeutet das, dass eine .NET-Sprache diese Typen nicht unterstützen muss. Infolgedessen sind auch unter Umständen keine Operatoren für diese Datentypen definiert, und es können keine mathematischen Operationen durchgeführt werden.

Wie der Tabelle 2.3 zu entnehmen ist, basieren alle Typen auf einer entsprechenden Definition im .NET Framework. Das hat zur Folge, dass anstelle der Angabe des C#-Alias zur Typbeschreibung auch der .NET-Laufzeittyp genannt werden kann. Damit sind die beiden folgenden Deklarationen der Variablen value absolut gleichwertig:

int value;
Int32 value;

Ganzzahlige Datentypen

C# stellt acht ganzzahlige Datentypen zur Verfügung, von denen vier vorzeichenbehaftet sind, der Rest nicht. Die uns interessierenden CLS-konformen Datentypen sind:

  • Byte
  • Int16
  • Int32
  • Int64

Int16, Int32 und Int64 haben einen Wertebereich, der nahezu gleichmäßig über die negative und positive Skala verteilt ist. Die vorzeichenlosen Datentypen, zu denen auch Byte gehört, decken hingegen nur den positiven Wertebereich, beginnend bei 0, ab. Der vorzeichenlose Typ Byte, der im Gegensatz zu SByte CLS-konform ist, ist insbesondere dann von Interesse, wenn auf binäre Daten zugegriffen wird.

Ganzzahlige Literale können in Dezimal- oder Hexadezimalform übergeben werden. Hexadezimale Zahlen (Basis = 16) erhalten zusätzlich das Präfix 0x. Die folgende Variable value beschreibt die Dezimalzahl 225:

int value = 0xE1;

Dezimalzahlen

Versuchen Sie einmal, die beiden folgenden Codezeilen zu kompilieren:

float value = 0.123456789;
Console.WriteLine(value);

Normalerweise würde man erwarten, dass der C#-Compiler daran nichts zu beanstanden hat. Dennoch zeigt er erstaunlicherweise einen Kompilierfehler an. Wie ist das zu erklären?

Auch ein Literal wie unsere Zahl 0,123456789 muss zunächst temporär in den Speicher geschrieben werden, bevor es endgültig der Variablen zugewiesen werden kann. Um eine Zahl im Speicher abzulegen, muss die Laufzeitumgebung aber eine Entscheidung treffen: Es ist die Entscheidung darüber, wie viel Speicherplatz dem Literal zugestanden wird. Das kommt aber auch der Festlegung auf einen bestimmten Datentyp gleich.

Literale, die eine Dezimalzahl beschreiben, werden von der .NET-Laufzeitumgebung als double-Typ angesehen.

Literale hingegen, die eine Ganzzahl beschreiben, werden von der Laufzeitumgebung als int (Int32) betrachtet.

Nun kommt es bei der Zuweisung unseres Literals an value jedoch zu einem Problem: Das Literal ist vom Typ double, und die Variable, die den Inhalt aufnehmen soll, ist vom Typ float. Per Definition weist double aber einen größeren Wertebereich als float auf – mit der Folge, dass unter Umständen vom Literal ein Wert beschrieben sein könnte, der größer ist als der, den ein float zu speichern vermag. Der Compiler verweigert deshalb diese Zuweisung.

Es gibt einen sehr einfachen Ausweg aus diesem Dilemma: Man hängt dazu an das Literal ein passendes Suffix an, hier F (oder gleichwertig f), mit dem wir den Typ float für das Literal erzwingen:

float value = 0.123456789F;
Console.WriteLine(value);

Nun ist der C#-Compiler in der Lage, den Inhalt an der Konsole anzuzeigen – vorausgesetzt, die Zahl entspricht dem Wertebereich eines float.

Tabelle 2.4 Typsuffix der Fließkommazahlen

Suffix Fließkommatyp

F oder f

float

D oder d

double

M oder m

decimal

Die Genauigkeit von Dezimalzahlen

Die drei Typen float, double und decimal, mit denen unter C# Fließkommazahlen dargestellt werden, beschreiben nicht nur unterschiedliche Wertebereiche, sondern auch – was im Grunde genommen noch viel wichtiger ist – unterschiedliche Genauigkeiten. Auf herkömmlichen Systemen beträgt die Genauigkeit eines float-Typs etwa zehn Stellen, die eines double-Typs etwa 16 Stellen, die eines decimal-Typs ca. 25–26. Abhängig ist die Genauigkeit dabei immer von der Anzahl der Ziffern des ganzzahligen Anteils der Dezimalzahl.

Die zeichenbasierten Datentypen »string« und »char«

Variablen vom Typ char können ein Zeichen des Unicode-Zeichensatzes aufnehmen. Unicode ist die Erweiterung des ein Byte großen ASCII- bzw. ANSI-Zeichensatzes mit seinen insgesamt 256 verschiedenen Zeichen. Unicode berücksichtigt die Bedürfnisse außereuropäischer Zeichensätze, für die eine Ein-Byte-Codierung nicht ausreichend ist. Jedes Unicode-Zeichen beansprucht zwei Byte, folglich ist der Unicode-Zeichensatz auch auf 65.536 Zeichen beschränkt. Die ersten 128 Zeichen (0–127) entsprechen denen des ASCII-Zeichensatzes, die folgenden 128 Zeichen beinhalten unter anderem Sonderzeichen und Währungssymbole.

Literale, die dem Typ char zugewiesen werden, werden in einfache Anführungsstriche gesetzt, z. B.:

char letter = 'A';

Um den ASCII-Wert eines einzelnen Zeichens zu erhalten, braucht man nur den Typ char einem Zahlentyp wie beispielsweise einem int zuzuweisen:

char letter = 'A';
int letterASCII = letter;
// Ausgabe: 65
Console.WriteLine(letterASCII);

Listing 2.11 Ermitteln des ASCII-Wertes eines Characters

Die implizite Umwandlung eines char in einen Zahlenwert bereitet anscheinend keine Probleme, der umgekehrte Weg – die Umwandlung eines Zahlenwerts in einen char – ist allerdings nicht ohne weiteres möglich.

char beschränkt sich nur auf ein Zeichen. Um eine Zeichenkette, die sich aus keinem oder bis zu maximal ca. 231 Einzelzeichen zusammensetzt, zu speichern oder zu bearbeiten, deklarieren Sie eine Variable vom Datentyp string. Die Einzelzeichen werden dabei wie bei char als Unicode-Zeichen der Größe 16 Bit behandelt. Zeichenketten werden grundsätzlich in doppelte Anführungsstriche gesetzt:

string str = "C# ist spitze."

Die Datentypen »Boolean«

Variablen vom Typ bool (Boolean) können nur zwei Zustände beschreiben, nämlich true oder false, z. B.:

bool flag = true;

false ist der Standardwert.

In vielen Programmiersprachen wird false numerisch mit 0 beschrieben und true durch alle Werte, die von 0 abweichen. .NET ist hier viel strenger, denn true ist nicht 1 und auch nicht 67, sondern ganz schlicht true.

Der Datentyp »Object«

Der allgemeinste aller Datentypen ist Object. Er beschreibt in seinen vier Byte einen Zeiger auf die Speicheradresse eines Objekts. Eine Variable dieses Typs kann jeden beliebigen anderen Datentyp beschreiben: Ob es sich um eine Zahl, eine Zeichenfolge, eine Datenbankverbindung oder um ein anderes Objekt wie zum Beispiel um die Schaltfläche in einem Windows-Fenster handelt, spielt dabei keine Rolle. Zur Laufzeit wird eine auf Object basierende Variable passend aufgelöst und die gewünschte Operation darauf ausgeführt.

Um das zu demonstrieren, ist im folgenden Codefragment eine Variable vom Typ object deklariert, der zuerst ein Zahlenliteral und anschließend eine Zeichenfolge zugewiesen wird:

object universal;
universal = 5;
Console.WriteLine(universal);
universal = "Hallo Welt.";
Console.WriteLine(universal);

Listing 2.12 Zuweisungen an eine Variable vom Typ »Object«

Die Variable universal verarbeitet beide Zuweisungen anstandslos – an der Konsole wird zuerst die Zahl 5 und danach die Zeichenfolge angezeigt.

Damit ist bei weitem noch nicht alles zum Typ Object gesagt. Es gibt noch zahlreiche andere Gesichtspunkte, die einer Erwähnung oder Diskussion würdig wären. Aber dazu müssen wir erst in die Tiefen der Objektorientierung gehen. Für den Moment ist eine oberflächliche Erwähnung des Typs Object völlig ausreichend.

Die einfachen Datentypen als Objekte

Eine Variable zu deklarieren, sieht harmlos und unscheinbar aus. Und dennoch, hinter dem Variablennamen verbergen sich Möglichkeiten, die Sie bisher vermutlich noch nicht erahnen. In der .NET-Laufzeitumgebung wird alles durch die objektorientierte Brille betrachtet – sogar die einfachen Datentypen.

Ein simpler Short soll ein Objekt sein? Wenn Sie dieser Aussage keinen Glauben schenken wollen, schreiben Sie folgende Codezeile:

Int16.

Beachten Sie bitte hierbei den Punkt, der auf Int16 folgt. Sie werden feststellen, dass hinter der Punktangabe eine Liste aufgeklappt wird, die IntelliSense-Unterstützung (siehe Abbildung 2.3).

Abbildung

Abbildung 2.3 IntelliSense-Unterstützung in der Entwicklungsumgebung

In dieser Liste sind alle Eigenschaften und Methoden aufgeführt, die den Typ Int16 auszeichnen. Sie können aus dem Angebot auswählen, wenn Sie mit den Pfeiltasten zu der gewünschten Funktionalität navigieren und dann die Tabulator-Taste drücken. Der ausgewählte Eintrag aus IntelliSense wird sofort vom Code übernommen, was den Vorteil hat, dass ein Schreibfehler ausgeschlossen ist.

Wenn Sie beispielsweise wissen wollen, wo die wertmäßige Ober- bzw. Untergrenze des Int16-Typs liegt, könnten Sie dies mit dem folgenden Codefragment abfragen:

Console.WriteLine("Int16(min) = {0}", Int16.MinValue);
Console.WriteLine("Int16(max) = {0}", Int16.MaxValue);

An der Konsole erfolgt danach die Anzeige:

Int16(min) = -32768
Int16(max) = 32767

Wahrscheinlich werden Sie schon festgestellt haben, dass IntelliSense nicht nur im Zusammenhang mit der Punktnotation funktioniert. Sobald Sie in einer Codezeile den ersten Buchstaben eintippen, wird IntelliSense geöffnet und bietet Ihnen alle programmierbaren Optionen an, auf die mit dem eingegebenen Buchstaben zugegriffen werden kann. Die Auswahl erfolgt analog wie oben beschrieben.


Rheinwerk Computing - Zum Seitenanfang

2.3.6 TypkonvertierungZur nächsten ÜberschriftZur vorigen Überschrift

Sehen wir uns die folgenden beiden Anweisungen in Listing 2.13 an:

int value1 = 12000;
long value2 = value1;

Listing 2.13 Zuweisung einer int-Variablen an eine long-Variable

Hier wird die Variable value1 vom Typ int deklariert und ihr ein Wert zugewiesen. Im zweiten Schritt erfolgt wiederum eine Variablendeklaration, diesmal vom Typ long. Der Inhalt der zuvor deklarierten Variablen value1 wird value2 zugewiesen. Der C#-Compiler wird beide Anweisungen anstandslos kompilieren.

Nun ändern wir die Reihenfolge in Listing 2.13 ab und deklarieren zuerst die long-Variable, weisen ihr den Wert von 12000 zu und versuchen dann, diese der int-Variablen zuzuweisen:

long value1 = 12000;
int value2 = value1;

Listing 2.14 Zuweisung einer long-Variablen an eine int-Variable

Diesmal ist das Ergebnis nicht wie vielleicht erwartet – der C#-Compiler quittiert die Zuweisung mit einer Fehlermeldung, obwohl der Wertebereich eines int die Zuweisung von 12000 eindeutig verkraftet. Das auftretende Problem beruht darauf, dass der Wertebereich eines int kleiner ist als der eines long. Im Gegensatz dazu ist die Zuweisung eines int an einen long eine zulässige Operation, weil der long einen größeren Wertebereich als int hat und somit weitaus höhere Werte verträgt.

Immer dann, wenn bei einer Operation zwei unterschiedliche Datentypen im Spiel sind, muss der Typ, der rechts vom Zuweisungsoperator steht, in den Typ umgewandelt werden, der sich auf der linken Seite befindet. Man spricht hierbei auch von der Konvertierung. Prinzipiell werden zwei Arten der Konvertierung unterschieden:

  • die implizite Konvertierung
  • die explizite Konvertierung

Die implizite Konvertierung

Eine implizite Konvertierung nimmt der C#-Compiler selbst vor. Implizit wird immer dann konvertiert, wenn der zuzuweisende Wert grundsätzlich immer kleiner oder gleich dem Datentyp ist, der den Wert empfängt. Schauen wir uns dazu Abbildung 2.4 an.

Abbildung

Abbildung 2.4 Die implizite Konvertierung einfacher Datentypen

Die Pfeilrichtung gibt eine implizite Konvertierung vor, entgegengesetzt der Pfeilrichtung wäre eine Konvertierung explizit. Demzufolge wird ein byte anstandslos implizit in einen short, int, long usw. konvertiert, aber nicht umgekehrt beispielsweise ein int in byte. Beachten Sie insbesondere, dass es keine impliziten Konvertierungen zwischen den Gleitkommatypen float/double und decimal gibt.

Eine besondere Stellung nehmen bool, string, char und object ein. Mit einem bool oder einem string sind keine impliziten Konvertierungen möglich, ein char kann mit Ausnahme von byte und short jedem anderen Typ zugewiesen werden. Variablen vom Typ object wiederum unterliegen Gesichtspunkten, die wir erst ab Kapitel 3 erörtern.

Die explizite Konvertierung

Unter expliziter Konvertierung versteht man die ausdrückliche Anweisung an den Compiler, den Wert eines bestimmten Datentyps in einen anderen umzuwandeln. Explizite Konvertierung folgt einer sehr einfachen Syntax: Vor dem zu konvertierenden Ausdruck wird in runden Klammern der Typ angegeben, in den die Konvertierung erfolgen soll, also:

(Zieldatentyp)Ausdruck

Man spricht bei den so eingesetzten runden Klammern auch vom Typkonvertierungsoperator.

Mit der expliziten Konvertierung wären die folgenden beiden Zuweisungen möglich:

float value1 = 3.12F;
decimal value2 = (decimal)value1;
byte value3 = 20;
char c = (char)value3;

Explizite Konvertierung mit den Methoden der Klasse »Convert«

Der expliziten Konvertierung mit dem Konvertierungsoperator sind Grenzen gesetzt. Beispielsweise bleibt ein boolescher Wert immer ein boolescher Wert. Damit ist die folgende Konvertierung unter C# falsch, obwohl sie in anderen Programmiersprachen durchaus zulässig ist:

int value = 1;
// fehlerhafte explizite Konvertierung
bool bolVar = (bool)value;

Um auch solche expliziten Konvertierungen zu ermöglichen, bietet die .NET-Klassenbibliothek die Klasse Convert an, die eine Reihe von Konvertierungsmethoden bereitstellt.

Tabelle 2.5 Die Konvertierungsmethoden der Klasse »Convert« (Auszug)

Methode Beschreibung

ToBoolean(Ausdruck)

Konvertiert den Ausdruck in einen bool-Typ.

ToByte(Ausdruck)

Konvertiert den Ausdruck in einen byte-Typ.

ToChar(Ausdruck)

Konvertiert den Ausdruck in einen char-Typ.

ToDecimal(Ausdruck)

Konvertiert den Ausdruck in einen decimal-Typ.

ToDouble(Ausdruck)

Konvertiert den Ausdruck in einen double-Typ.

ToInt16(Ausdruck)

Konvertiert den Ausdruck in einen short-Typ.

ToInt32(Ausdruck)

Konvertiert den Ausdruck in einen int-Typ.

ToInt64(Ausdruck)

Konvertiert den Ausdruck in einen long-Typ.

ToSByte(Ausdruck)

Konvertiert den Ausdruck in einen sbyte-Typ.

ToSingle(Ausdruck)

Konvertiert den Ausdruck in einen float-Typ.

ToString(Ausdruck)

Konvertiert den Ausdruck in einen string-Typ.

ToUInt16(Ausdruck)

Konvertiert den Ausdruck in einen ushort-Typ.

ToUInt32(Ausdruck)

Konvertiert den Ausdruck in einen uint-Typ.

ToUInt64(Ausdruck)

Konvertiert den Ausdruck in einen ulong-Typ.

Damit ist das Codefragment

long value1 = 4711;
int value2 = (int)value1;

gleichwertig mit:

long value1 = 4711;
int value2 = Convert.ToInt32(value1);

In zwei ganz wesentlichen Punkten unterscheidet sich die Konvertierung mit den Methoden der Convert-Klasse von der mit dem Konvertierungsoperator:

  • Es können Konvertierungen durchgeführt werden, die mit dem Typkonvertierungsoperator »()« unzulässig sind. Allerdings sind die Methoden der Klasse Convert nur auf elementare Datentypen beschränkt.
  • Grundsätzlich werden alle Konvertierungen mit den Methoden der Convert-Klasse auf einen eventuellen Überlauf hin untersucht.

Den letztgenannten Punkt werden wir im folgenden Abschnitt behandeln, während wir uns an dieser Stelle zunächst dem erstgenannten Punkt zuwenden. Angenommen, wir wollen an der Eingabeaufforderung die Eingabe in einer Integer-Variablen speichern, muss die Anweisung dazu wie folgt lauten:

int value = Convert.ToInt32(Console.ReadLine());

Bekanntlich liefert ReadLine die Benutzereingabe als Zeichenfolge vom Typ string zurück. Wäre die Methode Convert.ToInt32 gleichwertig mit dem Typkonvertierungsoperator, würde der C#-Compiler auch die folgende Anweisung anstandslos kompilieren:

int intDigit = (int)Console.ReadLine(); // FALSCH!!

Allerdings wird uns der Compiler diese Anweisung mit der Fehlermeldung

Konvertierung des Typs 'string' zu 'int' nicht möglich

quittieren, denn eine explizite Konvertierung des Typs string in einen numerischen Typ mit dem Typkonvertierungsoperator ist auch dann unzulässig, wenn die Zeichenfolge eine Zahl beschreibt. Nur die Methoden der Klasse Convert sind so geprägt, dass dennoch eine Konvertierung erfolgt. Natürlich muss die Konvertierung aus logischer Sicht sinnvoll sein. Solange aber eine Zeichenfolge eine Zahl beschreibt, darf auch eine Zeichenfolge in einen numerischen Typ überführt werden.

Bereichsüberschreitung infolge expliziter Konvertierung

Eine explizite Konvertierung lässt auch eine einengende Umwandlung zu, beispielsweise wenn ein long-Wert einer int-Variablen zugewiesen wird. Damit drängt sich sofort eine Frage auf: Was passiert, wenn der Wert der Übergabe größer ist als der Maximalwert des Typs, in den konvertiert wird? Nehmen wir dazu beispielsweise an, wir hätten eine Variable vom Typ short deklariert und ihr den Wert 436 zugewiesen. Nun soll diese Variable in den Typ byte überführt werden, der den Wertebereich zwischen 0 und 255 beschreibt.

short value1 = 436;
byte value2 = (byte)value1;
Console.WriteLine(value2);

Dieser Code resultiert in der folgenden Ausgabe:

180

Um zu verstehen, wie es zu dieser zunächst unverständlichen Ausgabe kommt, müssen wir uns die bitweise Darstellung der Zahlen ansehen. Für den Inhalt der Variablen value1 ist das:

436 = 0000 0001 1011 0100

Nach der Konvertierung liegt das Ergebnis 180 vor, beschrieben durch:

180 = 1011 0100

Vergleichen wir jetzt die bitweise Darstellung der beiden Zahlen, kommen wir sehr schnell zu der Erkenntnis, dass bei einer expliziten Konvertierung mit dem Typkonvertierungsoperator beim Überschreiten der Bereichsgrenze des Zieldatentyps die überschüssigen Bits einfach ignoriert werden. Aus dem verbleibenden Rest wird die neue Zahl gebildet.

Dieses Verhalten kann zu sehr schwer zu lokalisierenden, ernsthaften Fehlern in einer Anwendung führen. Wenn Sie Programmcode schreiben und explizit konvertieren müssen, sollten Sie daher die Kontrolle über einen eventuell eintretenden Überlauf haben. Unter C# gibt es dazu drei Alternativen:

  • die Operatoren checked und unchecked
  • eine entsprechende Einstellung im Projekteigenschaftsfenster
  • der Verzicht auf den Typkonvertierungsoperator und stattdessen die Verwendung der Klasse Convert

Die Operatoren »checked« und »unchecked«

Wenden wir uns zunächst den Schlüsselwörtern checked und unchecked zu, und schauen wir uns an einem Beispiel den Einsatz und die Wirkungsweise an:

// Beispiel: ..\Kapitel 2\CheckedSample
static void Main(string[] args) {
// Zahleneingabe anfordern
Console.Write("Geben Sie eine Zahl im Bereich von ");
Console.Write("0...{0} ein: ", Int16.MaxValue);
// Eingabe einem short-Typ zuweisen
short value1 = Convert.ToInt16(Console.ReadLine());
// Überlaufprüfung einschalten
byte value2 = checked((byte)value1);
Console.WriteLine(value2);
Console.ReadLine();
}

Listing 2.15 Arithmetischen Überlauf mit »checked« prüfen

Nach dem Starten der Anwendung wird der Benutzer dazu aufgefordert, eine Zahl im Bereich von 0 bis zum Maximalwert eines short einzugeben. Entgegengenommen wird die Eingabe durch die Methode Console.ReadLine, die ihrerseits die Eingabe als Zeichenfolge, also vom Typ string zurückliefert. Um die gewünschte Zahl einer short-Variablen zuweisen zu können, muss explizit konvertiert werden. Beachten Sie bitte, dass wir dazu die Methode ToInt16 der Klasse Convert einsetzen müssen, da eine Konvertierung eines string in einen short mit dem Typkonvertierungsoperator nicht zulässig ist:

short value1 = Convert.ToInt16(Console.ReadLine());

Gibt der Anwender eine Zahl ein, die den Wertebereich des short-Typs überschreitet, wird ein Laufzeitfehler ausgelöst und die Laufzeit der Anwendung beendet. Falls der Wertebereich nicht überschritten wird, wird die dann folgende Anweisung ausgeführt:

byte value2 = checked((byte)value1);

In dieser Anweisung steckt allerdings eine Gemeinheit, denn nun soll der Inhalt der short-Variablen einer byte-Variablen zugewiesen werden. Je nachdem, welche Zahl der Anwender eingegeben hat, wird die Zuweisung fehlerfrei erfolgen oder – bedingt durch die Überprüfung mit checked – zu einem Fehler führen. Löschen Sie checked aus dem Programmcode, wird die Zuweisung einer Zahl, die den Wertebereich eines byte-Typs überschreitet, keinen Fehler verursachen.

checked ist ein Operator und wird verwendet, um einen eventuell auftretenden arithmetischen Überlauf zu steuern. Tritt zur Laufzeit ein Überlauf ein, weil der Anwender eine Zahl eingegeben hat, die den Wertebereich des Typs überschreitet, in den konvertiert werden soll, wird ein Laufzeitfehler ausgelöst, der unter .NET auch als Ausnahme bzw. Exception bezeichnet wird. Geben wir beispielsweise an der Konsole die Zahl 436 ein, werden wir die folgende Mitteilung erhalten:

Nach dem Schließen der Fehlermeldung wird die Anwendung unplanmäßige beendet. Nun könnten Sie argumentieren, dass das Beenden der Laufzeitumgebung auch nicht das sein kann, was unbedingt erstrebenswert ist. Dieses Argument ist vollkommen richtig, aber Laufzeitfehler lassen sich mittels Programmcode abfangen, und die Anwendung bleibt danach in einem ordnungsgemäßen Laufzeitzustand. Diesem Thema werden wir uns in Kapitel 7 dieses Buches noch ausgiebig widmen.

Abbildung

Abbildung 2.5 Fehlermeldung durch Überlauf

Falls nicht nur ein einzelner Ausdruck, sondern mehrere Ausdrücke innerhalb eines Anweisungsblocks auf einen möglichen Überlauf hin kontrolliert werden sollen, können Sie hinter checked einen Anweisungsblock angeben, innerhalb dessen der unkontrollierte Überlauf durch die Auslösung eines Laufzeitfehlers unterbunden wird. Wie diese Variante von checked eingesetzt wird, können Sie dem nachfolgenden Beispiel entnehmen.

static void Main(string[] args) {
checked
{
short shortValue = 436;
int integerValue = 1236555;
byte byteValue = (byte)shtVar;
shortValue = (short)integerValue;
Console.WriteLine(byteValue);
Console.ReadLine();
}
}

Listing 2.16 Mehrere Ausdrücke gleichzeitig auf Überlauf hin prüfen

Wir können festhalten, dass wir mit checked eine gewisse Kontrolle ausüben können, falls zur Laufzeit bedingt durch die explizite Konvertierung ein Überlauf eintreten kann. Der Operator unchecked ist die Umkehrung der Arbeitsweise von checked, er schaltet die Überprüfung des Überlaufs aus und ist der Standard.

Während checked sich nur lokal auf den in runden Klammern stehenden Ausdruck bzw. einen eingeschlossenen Anweisungsblock bezieht, kann durch eine Änderung im Projekteigenschaftsfenster die Kontrolle über sämtliche auftretenden Überläufe in einer Anwendung ausgeübt werden. Öffnen Sie dieses Fenster, indem Sie im Projektmappen-Explorer das Projekt markieren, dessen Kontextmenü mit der rechten Maustaste öffnen und dann Eigenschaften wählen.

Das Projekteigenschaftsfenster wird als zusätzliche Lasche im Code-Editor angezeigt. Am linken Rand werden mehrere Auswahloptionen angeboten. Um unser Problem zu lösen, müssen Sie Erstellen auswählen. Im sich öffnenden Registerblatt sehen Sie rechts unten die Schaltfläche Erweitert. Klicken Sie darauf, wird ein Dialog geöffnet, der die von uns gesuchte Option anbietet: Auf arithmetischen Über-/Unterlauf überprüfen (Abbildung 2.6). Markieren Sie das Kontrollkästchen, um sicherzustellen, dass eine generelle Überprüfung auf eine Über- oder Unterschreitung des Wertebereichs erfolgt. Damit vermeiden Sie Datenverlust.

Mit dieser Einstellung kann man auf alle expliziten Angaben von checked verzichten, denn die Überprüfung des Unter- bzw. Überlaufs wird in der Anwendung zum Standard erklärt. Möchte man aus bestimmten Gründen auf die Überprüfung verzichten, kommt der Operator unchecked ins Spiel und hebt für den entsprechenden Ausdruck die Überprüfung wieder auf.

Abbildung

Abbildung 2.6 Einstellen der standardmäßigen Überprüfung des Überlaufs im Projekteigenschaftsfenster



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Visual C# 2012

Visual C# 2012
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Rheinwerk-Shop: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: C/C++






 C/C++


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo





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


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern