C# - Basics


... [ Seminar WS 2002/03 ] ... [ C# ] ... [ .NET Framework ] ... [ C# - Basics ] ... [ Vergleich mit Java ]

Übersicht: C# - Basics


Kompilieren und Ausführen

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

 


Namespaces, Using-Klausel

'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.


Grundlegende Datentypen, Boxing/Unboxing

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 {

public Boxing() {

int i = 123; //einfacher Datentyp

object box = i; //Objekttyp - Boxing
int j = (int)box; //Unboxing

}

}

 
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.


Object als Basisklasse

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 {

public Object();

public static bool Equals(object objA, object objB);
public static bool ReferenceEquals(object objA, object objB);

public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual string ToString();

protected override void Finalize();
protected object MemberwiseClone();

}
 
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).


Präprozessor

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
#define RELEASE
#if DEBUG && RELEASE
#warning RELEASE und DEBUG gleichzeitig gesetzt
#endif
 
Bedingte Kompilierung (Präprozessor) - Listing 3.4

... [ Seminar WS 2002/03 ] ... [ C# ] ... [ .NET Framework ] ... [ C# - Basics ] ... [ Vergleich mit Java ]