Hat man das .NET-Framework SDK installiert [Microsoft(7)],
so kann man C# Quellcode kompilieren. Dem .NET-Framework SDK liegt ein Compiler
mit Aufruf 'csc' bei. Der Compiler erzeugt eine Quelldatei (.exe), die den in
die MSIL (Microsoft Intermediate Language)übersetzten Quellcode enthält.
Zum Ausführen werden die Bibliotheken der CLR (Common Language Runtime)
benötigt, die dem .NET-Framework beiliegen.
Kompilieren und Ausführen
- Abbildung 3.1 |
'namespace'-Deklarationen sind Namensräume, die Klassen umschließen. Diese Namensräume sind vergleichbar zu den Packages in Java. Namensräume können ineinander geschachtelt oder mit Punkt getrennt werden. In einer Quelldatei können beliebig viele Namensräume definiert werden.
C# | |
namespace Mein.Eigener.Namespace { class A { [...] } //oder namespace Mein { namespace Eigener { namespace Namespace { class A { [...] } } } } |
|
geschachtelte Namensräume
- Listing 3.1 |
Die 'using'-Klausel bindet einen Namensraum ein und entspricht dem 'import' in Java.
Typ | Byte | Laufzeittyp | Beschreibung |
byte | 1 | Byte | Byte-Wert ohne Vorzeichen |
sbyte(*) | 1 | SByte | Byte-Wert mit Vorzeichen |
short | 2 | Int16 | Short-Wert mit Vorzeichen |
ushort(*) | 2 | UInt16 | Short-Wert ohne Vorzeichen |
int | 4 | Int32 | Integer-Wert mit Vorzeichen |
uint(*) | 4 | UInt32 | Integer-Wert ohne Vorzeichen |
long | 8 | Int64 | Langer Integer-Wert mit Vorzeichen |
ulong(*) | 8 | UInt64 | Langer Integer-Wert ohne Vorzeichen |
float | 4 | Single | Gleitkommazahl |
double | 8 | Double | Gleitkommazahl mit doppelter Genauigkeit |
decimal(*) | 16 | Decimal | Zahl mit hoher fester Genauigkeit |
string | String | Unicode-Zeichenfolge | |
char | Char | Unicode-Zeichen | |
bool | Boolean | Boolescher Wert | |
Tabelle grundlegender Datentypen in C# - Abbildung
3.2 |
blaue Felder entsprechen Werttypen / rote Felder sind Referenztypen
(*) Typ hat kein Pendant in Java
Soll ein Wertetyp als Referenztyp fungieren wird ein Wrapper benötigt, der den Wert in ein Objekt packt (Boxing). Dieses Objekt wird dann dem Heap zugewiesen. Das Boxing geschieht in C# automatisch. Unter Unboxing versteht man den umgekehrten Vorgang, nämlich das Entpacken des Wertes aus seiner Objekthülle.
C# | |
class Boxing { } |
|
Boxing / Unboxing
primitiver Typen - Listing 3.2 |
Warum hier beim Unboxing ein Casting-Operator nötig ist bleibt unklar, denn die Typinformation ist bei der Zuweisung bekannt, also könnte hier auch das Unboxing automatisch geschehen.
In C# gibt es den Typ 'object' welcher ein Alias auf 'System.Object' des .NET Frameworks ist. 'Object' ist die grundlegende Basisklasse für Referenz- als auch für Werttypen. Referenztypen werden auf dem Heap gespeichert und besitzen einen 8 Byte "Overhead". In diesem Overhead wird der Typ des Objekts, sowie Information über Synchronisationszustand (bei Threads) und Garbage-Collector. Werttypen, die auf dem Stack gespeichert werden besitzen diesen Overhead nicht.
Alle Referenz- (Classes) und Werttypen (Structs, Enums) sind implizit von 'Object'
abgeleitet.
In 'Object' sind folgende Methoden implementiert:
C# | |
//aus Namensraum System public class Object { |
|
Die ultimative Basisklasse
'System.Object' - Listing 3.3 |
Die Methode 'Equals' testet das übergebene Objekt auf referenzielle Gleichheit.
Bei Werttypen ist diese Methode überschrieben, so dass hier die Werte der
Objekte verglichen werden (liefert also 'true' bei gleichem Objektinhalt, auch
wenn die Objekte an verschiedenen Speicheradressen liegen).
'ReferenceEquals' testet ausnahmslos auf referenzielle Gleichheit, also bei
Referenz-, als auch bei Werttypen. 'ReferenceEquals' ist nicht virtuell, kann
somit nicht überschrieben werden und bietet den einzig sicheren Test auf
Referenzgleichheit.
'GetHashCode' liefert einen Hash-Code, so dass 'object' mit Hashtables des Typs
'System.Collections.Hashtable' benutzt werden kann.
'GetType' liefert interne Metadaten des Objekts als 'Type'-Objekt (ähnlich
dem 'typeof'-Operator). 'Type' ist eine abstrakte Klasse die alle Metadaten
für alle .NET-Typen beinhaltet (Stichwort Reflection).
'ToString' liefert einen 'String', der das Objekt repräsentiert - als Standard
steht in dem String der Bezeichner des Objekts. Die meisten Klassen überschreiben
die Methode, so dass 'ToString' dann den Bezeichner des Objekts sowie dessen
Inhalt ausgibt (formatierte Ausgabe).
In C# gibt es, ähnlich wie in C oder C++, Präprozessor-Direktiven, diese dienen in C# jedoch ausschließlich zur bedingten Kompilierung. Eine Textersetzung, wie sie aus den anderen "Familienmitgliedern" bekannt ist, ist somit nicht möglich.
Quellcode wird abhängig vom Wert des hinter '#if' angegebenen Ausdrucks
in die Kompilierung einbezogen. Kann der Ausdruck hinter #if zu true ausgewertet
werden, wird der Teil im Quellcode bis zur nachfolgenden Anweisung in die Kompilierung
einbezogen. Mit '#define' / '#undefine' können Symbole für die bedingte
Kompilierung gesetzt bzw. entfernt werden.
Die Direktiven '#warning', '#error' erzeugen zur Compile-Zeit eine Warn- bzw.
Fehlermeldung
C# | |
#define DEBUG #if DEBUG && RELEASE #warning RELEASE und DEBUG gleichzeitig gesetzt #endif |
|
Bedingte Kompilierung
(Präprozessor) - Listing 3.4 |