Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 35 C# im Vergleich zu anderen Sprachen
  gp 35.1 Unterschiede zwischen C# und C/C++
  gp 35.2 Unterschiede zwischen C# und Java
  gp 35.3 Unterschiede zwischen C# und Visual Basic 6
  gp 35.4 Codeaussehen
  gp 35.5 Weitere .NET-Sprachen

Kapitel 35 C# im Vergleich zu anderen Sprachen

In diesem Kapitel soll C# mit anderen Sprachen verglichen werden. C#, C++ und Java gehen auf dieselben Wurzeln zurück und ähneln sich daher stärker als viele andere Sprachen. Visual Basic ist C# nicht so ähnlich wie die zuvor genannten Sprachen, weist jedoch immer noch einige übereinstimmende syntaktische Elemente auf.

In einem gesonderten Abschnitt dieses Kapitels werden die .NET-Versionen von Visual C++ und Visual Basic besprochen, da diese sich von ihren jeweiligen Vorgängerversionen ebenfalls unterscheiden.


Galileo Computing

35.1 Unterschiede zwischen C# und C/C++  downtop

Der C#-Code wird C- und C++-Programmierern vertraut vorkommen, dennoch gibt es einige wesentliche und verschiedene weniger bedeutende Unterschiede. Im Folgenden erhalten Sie einen Überblick über diese Unterschiede. Einen detaillierteren Vergleich finden Sie im entsprechenden Microsoft-Whitepaper, C# for the C++ Programmer.


Galileo Computing

35.1.1 Eine verwaltete Umgebung  downtop

C# wird in der .NET-Laufzeitumgebung ausgeführt. Dies bedeutet nicht nur, dass viele Vorgänge nicht der Steuerung des Programmierers unterliegen, gleichzeitig wird auch ein brandneuer Satz Frameworks bereitgestellt. Alles in allem ergeben sich hierdurch verschiedene Änderungen.

gp  Das Löschen von Objekten erfolgt über die Speicherbereinigung und wird ausgeführt, wenn das Objekt nicht länger benötigt wird. Destruktoren (= Finalisierungsroutinen) können zur Durchführung von Bereinigungsaufgaben eingesetzt werden, dies jedoch nicht im gleichen Umfang wie bei C++-Destruktoren.
gp  In C# gibt es keine Zeiger. Nun, es gibt Zeiger im unsafe-Modus, diese werden jedoch selten eingesetzt. Statt dessen werden Verweise verwendet, die denen von C++ ähneln, jedoch nicht alle der C++-Einschränkungen aufweisen.
gp  Quellen werden in Assemblierungen kompiliert, die sowohl den kompilierten Code (ausgedrückt in der .NET-Zwischensprache IL) als auch Metadaten zur Beschreibung des kompilierten Codes enthalten. Alle .NET-Sprachen fragen über die Metadaten die gleichen Informationen ab, wie sie in den C++-.h-Dateien enthalten sind; die include-Dateien fallen weg.
gp  Das Aufrufen von systemeigenem Code ist etwas zeitaufwendiger.
gp  Es ist keine C/C++-Laufzeitbibliothek vorhanden. Die hiermit ausgeführten Operationen, beispielsweise die Zeichenfolgenbearbeitung, E/A-Operationen und andere Routinen, können mit dem .NET-Laufzeitsystem ausgeführt werden und befinden sich in dem Namespace, dessen Name mit System beginnt.
gp  Anstelle einer Fehlerrückgabe wird die Ausnahmebehandlung verwendet.

Galileo Computing

35.1.2 .NET-Objekte  downtop

C#-Objekte gehen allesamt auf die Basisklasse object zurück, daher ist nur eine Einfachvererbung von Klassen möglich, obwohl das mehrfache Implementieren von Schnittstellen zulässig ist.

Kleine, schlanke Objekte, beispielsweise Datentypen, können als Strukturen deklariert werden (so genannte Wertetypen), d. h., sie werden nicht dem Heap, sondern einem Stack zugeordnet.

C#-Strukturen und andere Wertetypen (einschließlich der integrierten Datentypen) können in Situationen eingesetzt werden, in denen ein Objektboxing erforderlich ist. Bei diesem Vorgang werden die Werte in einen dem Heap zugeordneten Wrapper kopiert, der mit den dem Heap zugeordneten Objekten kompatibel ist (auch Verweisobjekte genannt). Dies vereinheitlicht das Typensystem und ermöglicht die Verwendung von Variablen als Objekte. Gleichzeitig entsteht kein Overhead, wenn eine Vereinheitlichung nicht erforderlich ist.

C# unterstützt Eigenschaften und Indizierer zum Trennen des Benutzermodells eines Objekts von der Objektimplementierung und unterstützt Zuweisungen und Ereignisse zum Kapseln der Funktionalität von Zeigern und Rückrufen.

C# stellt das params-Schlüsselwort bereit, um eine den vararg-Funktionen ähnliche Unterstützung zu bieten.


Galileo Computing

35.1.3 C#-Anweisungen  downtop

C#-Anweisungen ähneln den C++-Anweisungen in vielerlei Hinsicht. Es gibt zwei merkliche Unterschiede:

gp  Das new-Schlüsselwort bedeutet »Abrufen einer neuen Kopie von«. Das Objekt ist dem Heap zugeordnet, wenn es sich um einen Verweistyp handelt, und ist dem Stack oder intern zugeordnet, wenn es sich um einen Wertetyp handelt.
gp  Alle Anweisungen, mit denen eine boolesche Bedingung geprüft wird, erfordern jetzt eine Variable vom Typ bool. Es findet keine automatische Konvertierung von int in bool statt, daher ist if (i) unzulässig.
gp  Switch-Anweisungen verhindern Fehlschläge, um die Fehlerzahl zu verringern. Switch kann auch für Zeichenfolgenwerte verwendet werden.
gp  Für das Durchlaufen von Objekten und Auflistungen kann foreach eingesetzt werden.
gp  Über checked und unchecked werden arithmetische Operationen und Konvertierungen auf einen Überlauf geprüft.
gp  Feste Zuordnungen erfordern, dass Objekte vor der Verwendung über einen festen Wert verfügen.

Galileo Computing

35.1.4 Attribute  downtop

Attribute dienen der Weitergabe beschreibender Daten vom Programmierer an anderen Code. Bei diesem Code kann es sich um die Laufzeitumgebung, einen Designer, ein Tool zur Codeanalyse oder um ein benutzerdefiniertes Tool handeln. Attributinformationen werden über einen Vorgang abgerufen, der als Reflektion bezeichnet wird.

Attribute werden von eckigen Klammern umschlossen und können für Klassen, Mitglieder, Parameter und weitere Codeelemente gesetzt werden. Beispiel:

[CodeReview("1/1/199", Comment="Rockin'")]
class Test
{
}

Galileo Computing

35.1.5 Versionssteuerung  downtop

C# ermöglicht gegenüber C++ eine verbesserte Versionssteuerung. Da das Laufzeitsystem den Mitgliedsentwurf handhabt, stellt die binäre Kompatibilität kein Problem dar. Die Laufzeit bietet, falls gewünscht, die Möglichkeit zur Verwendung nebeneinander existierender Komponentenversionen sowie eine geeignete Semantik zur Versionssteuerung für Frameworks, C# ermöglicht dem Programmierer die Angabe des beabsichtigten Versionszwecks.


Galileo Computing

35.1.6 Codeorganisation  downtop

C# verwendet keine Headerdateien, sämtlicher Code wird intern geschrieben, und während eine Präprozessorunterstützung für bedingten Code vorhanden ist, werden Makros nicht unterstützt. Die Einschränkungen ermöglichen dem Compiler eine schnellere Analyse des C#-Codes und ermöglichen es darüber hinaus einer Entwicklungsumgebung, den C#-Code besser zu verstehen.

Zusätzlich liegen im C#-Code weder Reihenfolgenabhängigkeiten noch Vorwärtsdeklarationen vor. Die Reihenfolge der Klassen in den Quelldateien ist unerheblich, die Klassen können nach Wunsch umgestellt werden.


Galileo Computing

35.1.7 Fehlende C++-Funktionen  downtop

Die folgenden C++-Funktionen stehen in C# nicht zur Verfügung:

gp  Mehrfachvererbung
gp  Const-Mitgliedsfunktionen oder -Parameter. Const-Felder werden unterstützt.
gp  Globale Variablen
gp  Typedef
gp  Konvertierung durch Erstellungsroutine
gp  Standardargumente für Funktionsparameter

Galileo Computing

35.2 Unterschiede zwischen C# und Java  downtop

C# und Java gehen auf gleiche Wurzeln zurück, daher ist es nicht überraschend, dass zwischen den beiden Sprachen Ähnlichkeiten vorhanden sind. Dennoch gibt es auch einige Unterschiede. Der größte Unterschied besteht darin, dass sich C# oberhalb der.NET-Frameworks und der .NET-Laufzeit befindet und Java den Frameworks und der Laufzeit von Java übergeordnet ist.


Galileo Computing

35.2.1 Datentypen  downtop

C# verfügt über primitivere Datentypen als Java. In der folgenden Tabelle werden die Java-Typen und deren C#-Äquivalente zusammengefasst:

C#-Typ Java-Typ Kommentar
sbyte byte C#-byte hat kein Vorzeichen
short short  
int int  
long long  
bool Boolean  
float float  
double double  
char char  
string string  
object object  
byte Byte-Wert ohne Vorzeichen  
ushort Short-Wert ohne Vorzeichen  
uint Integer-Wert ohne Vorzeichen  
ulong Long-Wert ohne Vorzeichen  
decimal Finanzdaten-/Währungstyp  

In Java werden die primitiven und die objektbasierten Datentypen voneinander getrennt. Damit die primitiven Typen an der objektbasierten Welt teilhaben können (beispielsweise in einer Auflistung), müssen sie in einer Instanz einer Wrapperklasse platziert werden, und die Wrapperklasse wird anschließend in der Auflistung platziert.

C# geht dieses Problem anders an. In C# sind die primitiven Typen (wie bei Java) dem Stack zugeordnet, sie werden jedoch auch als von der ultimativen Basisklasse object abgeleitet betrachtet. Dies bedeutet, dass für die primitiven Typen Mitgliedsfunktionen definiert und aufgerufen werden können. Mit anderen Worten, der folgende Code ist zulässig:

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine(5.ToString());
    }
}

Die Konstante 5 weist den Typ int auf, das ToString()-Mitglied ist für den Typ int definiert, daher kann der Compiler einen Aufruf generieren und den int an die Mitgliedsfunktion übergeben, als handele es sich um ein Objekt.

Dies funktioniert prima, wenn der Compiler die Handhabung eines primitiven Typs kennt, jedoch nicht, wenn ein primitiver Typ mit den dem Heap zugeordneten Objekten einer Auflistung zusammenarbeiten muss. Immer dann, wenn ein primitiver Typ verwendet wird und ein Parameter vom Typ object erforderlich ist, führt der Compiler ein automatisches Boxing des primitiven Typs in einen dem Heap zugeordneten Wrapper durch. Hier ein Beispiel zum Boxing:

using System;
class Test
{
    public static void Main()
    {
        int v = 55;
        object o = v;        // Boxing von v in o
        Console.WriteLine("Value is: {0}", o);
        int v2 = (int) o;    // Unboxing zurück in int
    }
}

In diesem Codeabschnitt wird der integer-Wert in ein object geboxt und der Mitgliedsfunktion Console.WriteLine() als Objektparameter übergeben. Das Deklarieren der Objektvariable erfolgt nur zur Verdeutlichung; im tatsächlichen Code würde v direkt übergeben, das Boxing würde auf Aufruferseite erfolgen. Der geboxte int-Wert kann durch eine cast-Operation extrahiert werden.


Galileo Computing

35.2.2 Erweitern des Typensystems  downtop

Die primitiven C#-Typen (mit Ausnahme von string und object) werden auch als Wertetypen bezeichnet, da Variablen dieser Typen tatsächliche Werte enthalten. Andere Typen werden Verweistypen genannt, da die zugehörigen Variablen Verweise enthalten.

In C# kann ein Programmierer das Typensystem durch Implementierung eines benutzerdefinierten Wertetyps erweitern. Diese Typen werden mit Hilfe des Schlüsselwortes struct implementiert und verhalten sich ähnlich wie die integrierten Wertetypen. Sie sind dem Stack zugeordnet, können Mitgliedsfunktionen aufweisen, und bei Bedarf wird ein Boxing oder Unboxing durchgeführt. Tatsächlich sind alle primitiven C#-Typen als Wertetypen implementiert, der einzige syntaktische Unterschied zwischen den integrierten Typen und den benutzerdefinierten Typen besteht darin, dass die integrierten Typen als Konstanten geschrieben werden können.

Damit sich die benutzerdefinierten Typen natürlich verhalten, können C#-Strukturen arithmetische Operatoren überladen, um numerische Operationen und Konvertierungen auszuführen, d. h., zwischen Strukturen und anderen Typen können implizite und explizite Konvertierungen erfolgen. C# unterstützt des Weiteren auch das Überladen von Klassen.

Ein struct wird mit Hilfe der gleichen Syntax geschrieben wie class, abgesehen davon, dass eine Struktur (neben der impliziten Basisklasse object) keine Basisklasse besitzen kann. Schnittstellen können jedoch implementiert werden.


Galileo Computing

35.2.3 Klassen  downtop

C#-Klassen ähneln den Java-Klassen sehr stark. Im Hinblick auf Konstanten, Basisklassen und Erstellungsroutinen, statische Konstruktoren, virtuelle Funktionen, Ausblenden und Versionssteuerung, Mitgliedszugriff, ref- und out-Parameter sowie das Identifizieren von Typen bestehen jedoch Unterschiede.

Konstanten

Java verwendet zum Deklarieren einer Klassenkonstante static final. In C# wird static final durch const ersetzt. Zusätzlich fügt C# das Schlüsselwort readonly hinzu, das verwendet wird, wenn die Konstantenwerte zur Kompilierungszeit nicht ermittelt werden können. Readonly-Felder können nur über eine Initialisierungsroutine oder eine Klassenerstellungsroutine gesetzt werden.

Basisklassen und Erstellungsroutinen

C# verwendet sowohl für das Definieren der Basisklasse und die Schnittstellen einer Klasse als auch für das Aufrufen weiterer Erstellungsroutinen die C++-Syntax. Eine C#-Klasse kann folgendermaßen aussehen:

class MyObject: Control, IFormattable
{
    public Control(int value)
    {
        this.value = value;
    }
        public Control() : base(value)
    {
    }
    int value;
}

Statische Erstellungsroutinen

Anstelle eines statischen Initialisierungsblocks verwendet C# statische Erstellungsroutinen, die mit Hilfe des Schlüsselworts static geschrieben werden, das einer parameterlosen Erstellungsroutine vorangestellt wird.

Virtuelle Funktionen, Ausblenden und Versionssteuerung

In C# sind standardmäßig alle Funktionen nicht virtuell, um eine virtuelle Funktion zu erhalten, muss explizit virtual angegeben werden. Aufgrund dieser Tatsache gibt es keine finalen Methoden in C#, auch wenn das Äquivalent zu einer finalen Klasse mit Hilfe von sealed erreicht werden kann.

C# bietet eine bessere Versionsunterstützung als Java, woraus sich einige kleinere Änderungen ergeben. Die Methodenüberladung erfolgt statt nach Signatur nach Name, d. h., das Hinzufügen von Klassen zu einer Basisklasse wirkt sich nicht auf das Programmverhalten aus. Sehen Sie sich folgendes Beispiel an:

public class B
{
}
public class D: B
{
    public void Process(object o) {}
}
class Test
{
    public static void Main()
    {
        D d = new D();
        d.Process(15);    // Aufruf durchführen
    }
}

Wenn der Provider der Basisklasse eine Verarbeitungsfunktion mit größerer Übereinstimmung hinzufügt, ändert sich das Verhalten:

public class B
{
    public void Process(int v) {}
}
public class D: B
{
    public void Process(object o) {}
}
class Test
{
    public static void Main()
    {
        D d = new D();
        d.Process(15);    // Aufruf durchführen
    }
}

In Java führt dies zum Aufruf der Klassenimplementierung, was wahrscheinlich falsch ist. In C# setzt das Programm seine Arbeit fort.

Zur Handhabung des ähnlichen Falles für virtuelle Funktionen muss in C# die Versionssemantik explizit angegeben werden. Wenn es sich bei Process() um eine virtuelle Funktion der abgeleiteten Klasse handelt, würde Java annehmen, dass es sich bei jeder Basisklassenfunktion mit übereinstimmender Signatur um eine Basis für die virtuelle Funktion handelt, was in den meisten Fällen nicht stimmt.

In C# werden virtuelle Funktionen nur außer Kraft gesetzt, wenn das Schlüsselwort override verwendet wird. Weitere Informationen hierzu finden Sie in Kapitel 11, Versionssteuerung mit new und override.

Mitgliedszugriff

Neben den Zugriffsbezeichnern public, private und protected steht in C# der Zugriffsbezeichner internal zur Verfügung. Auf Mitglieder mit dem Zugriff internal kann von Klassen desselben Projekts aus, jedoch nicht von außerhalb des Projekts zugegriffen werden.

Ref- und Out-Parameter

In Java werden Parameter immer nach Wert übergeben. C# ermöglicht mit dem Schlüsselwort ref das Übergeben von Parametern als Verweis. Dies ermöglicht der Mitgliedsfunktion, den Wert des Parameters zu ändern.

C# bietet darüber hinaus die Möglichkeit, Parameter mit Hilfe des out-Schlüsselwortes zu definieren, das genau wie ref funktioniert, abgesehen davon, dass die als Parameter übergebene Variable den Wert nicht vor dem Aufruf kennen muss.

Identifizieren von Typen

Java verwendet die Methode GetClass(), um ein Class-Objekt mit Informationen zum aufgerufenen Objekt zurückzugeben. Das Type-Objekt ist das .NET-Äquivalent zum Class-Objekt und kann auf verschiedene Weise abgerufen werden:

gp  Durch Aufrufen der GetType()-Methode für eine Objektinstanz
gp  Durch Verwenden des typeof-Operators für den Typennamen
gp  Durch Ermitteln des Typs nach Name mit der Klasse System.Reflection

Galileo Computing

35.2.4 Schnittstellen  downtop

Während Java-Schnittstellen Konstanten besitzen können, ist dies in C# nicht möglich. Bei der Implementierung von Schnittstellen stellt C# eine explizite Schnittstellenimplementierung bereit. Dies ermöglicht einer Klasse die Implementierung zweier Schnittstellen unterschiedlicher Quelle und gleichem Mitgliedsnamen. Die explizite Schnittstellenimplementierung kann auch dazu verwendet werden, Schnittstellenimplementierungen vor dem Benutzer zu verbergen. Weitere Informationen zu diesem Thema finden Sie in Kapitel 10, Schnittstellen.


Galileo Computing

35.2.5 Eigenschaften und Indizierer  downtop

In Java-Programmen werden Eigenschaften häufig durch das Deklarieren von get- und set-Methoden eingesetzt. In C# erscheint eine Eigenschaft dem Benutzer einer Klasse als Feld, bietet jedoch get- und set-Zugriffsroutinen zum Durchführen von Lese- oder Schreiboperationen.

Ein Indizierer ähnelt einer Eigenschaft, statt jedoch wie ein Feld auszusehen, wird der Indizierer dem Benutzer als Array angezeigt. Wie die Eigenschaften besitzen Indizierer get- und set-Zugriffsroutinen; im Gegensatz zu den Eigenscahften kann ein Indizierer jedoch für verschiedene Typen überladen werden. Dies ermöglicht das Indizieren von Datenbankzeilen nach Spaltenzahl und Spaltenname sowie das Indizieren von Hashtabellen nach Hashschlüssel.


Galileo Computing

35.2.6 Zuweisungen und Ereignisse  downtop

Wenn ein Objekt in Java einen Rückruf benötigt, wird mit einer Schnittstelle angegeben, wie das Objekt gebildet wird, und eine Methode innerhalb der Schnittstelle wird für den Rückruf aufgerufen. Bei C#-Schnittstellen kann ein ähnlicher Ansatz verwendet werden.

C# stellt Zuweisungen bereit, die man sich wie typensichere Funktionszeiger vorstellen kann. Eine Klasse kann eine Zuweisung für eine Funktion der Klasse erstellen, anschließend kann die Zuweisung an eine Funktion übergeben werden, die diese Zuweisung akzeptiert. Anschließend kann die Funktion die Zuweisung aufrufen.

C# baut auf Zuweisungen mit Ereignissen auf, die von den .NET-Frameworks verwendet werden. Ereignisse implementieren das Veröffentlichen-Abonnieren-Prinzip; wenn ein Objekt (beispielsweise ein Steuerelement) ein Klickereignis unterstützt, kann eine beliebige Anzahl weiterer Klassen eine Zuweisung registrieren, die bei Auslösen des Ereignisses aufgerufen werden soll.


Galileo Computing

35.2.7 Attribute  downtop

Attribute dienen der Weitergabe beschreibender Daten vom Programmierer an anderen Code. Bei diesem Code kann es sich um die Laufzeitumgebung, einen Designer, ein Tool zur Codeanalyse oder um ein benutzerdefiniertes Tool handeln. Attributinformationen werden über einen Vorgang abgerufen, der als Reflektion bezeichnet wird.

Attribute werden von eckigen Klammern umschlossen und können für Klassen, Mitglieder, Parameter und weitere Codeelemente gesetzt werden. Beispiel:

[CodeReview("1/1/199", Comment="Rockin'")]
class Test
{
}

Galileo Computing

35.2.8 Anweisungen  downtop

Die C#-Anweisungen werden dem Java-Programmierer bekannt vorkommen, es gibt jedoch einige neue Anweisungen und ein paar Unterschiede bei den vorhandenen Anweisungen zu beachten.

import kontra using

Die import-Anweisung wird in Java dazu eingesetzt, ein Paket zu ermitteln und die Typen in die aktuelle Datei zu importieren.

In C# wird diese Operation aufgeteilt. Die Assemblierungen, von denen ein Codeabschnitt abhängt, müssen explizit angegeben werden, entweder über die Befehlszeile mit Hilfe der Option /r oder in der Visual Studio-IDE. Die grundlegenden Systemfunktionen (aktuell die in mscorlib.dll enthaltenen Funktionen) sind die einzigen, die automatisch vom Compiler importiert werden.

Nachdem eine Assemblierung referenziert wurde, können die enthaltenen Typen verwendet werden, müssen jedoch unter Verwendung des vollqualifizierten Namens angegeben werden. Die reguläre Ausdrucksklasse heißt beispielsweise System.Text.RegularExpressions.Regex. Dieser Klassenname kann direkt verwendet werden, oder es werden mit Hilfe der using-Anweisung die Typen eines Namespaces in den Namespace oberster Ebene importiert. Mit der folgenden using-Klausel

using System.Text.RegularExpressions;

kann die Klasse einfach durch Einsatz von Regex angegeben werden. Es gibt außerdem eine Variante der using-Anweisung, die zur Vermeidung von Namenskollisionen das Angeben eines Typenalias ermöglicht.

Überlauf

Java kann weder bei Konvertierungen noch bei mathematischen Fehlern Überläufe ermitteln.

In C# kann eine solche Ermittlung durch die checked- und unchecked-Anweisungen und Operatoren gesteuert werden. Konvertierungen und mathematische Operationen, die in einem als checked deklarierten Kontext erfolgen, erzeugen Ausnahmen, wenn die Operation zu einem Überlauf oder Fehlern führt; die Ausführung einer solchen Operation in einem als unchecked deklarierten Kontext führt nie zu einer Fehlerausgabe. Der Standardkontext wird durch das Compilerflag /checked gesteuert.

Unsicherer Code

Der so genannte unsichere Code in C# ermöglicht die Verwendung von Zeigervariablen und wird eingesetzt, wenn die Leistung extrem wichtig ist oder eine Integration mit vorhandener Software benötigt wird, beispielsweise mit COM-Objekten oder systemeigenem Code in DLLs. Die fixed-Anweisung wird dazu verwendet, ein Objekt »festzunageln«, damit es bei einer Speicherbereinigung nicht verschoben wird.

Da von der Laufzeit nicht geprüft werden kann, ob der unsichere Code gefahrlos ausgeführt werden kann, kann eine Ausführung nur erfolgen, wenn dem Code vom Laufzeitsystem vertraut wird. Dies verhindert eine Ausführung in Downloadszenarien.

Zeichenfolgen

Das C#-Zeichenfolgenobjekt kann für den Zugriff auf bestimmte Zeichen indiziert werden. Beim Zeichenfolgenvergleich werden nicht die Zeichenfolgenverweise, sondern die Zeichenfolgenwerte miteinander verglichen.

Zeichenfolgenliterale weisen in C# ebenfalls Unterschiede auf; C# unterstützt Escapezeichen innerhalb von Zeichenfolgen, die zum Einfügen von Sonderzeichen verwendet werden. Die Zeichenfolge \t wird beispielsweise in ein Tabulatorzeichen übersetzt.

Dokumentation

Die XML-Dokumentation in C# ähnelt Javadoc, C# gibt jedoch nicht die Struktur der Dokumentation vor, und der Compiler prüft die Richtigkeit der Dokumentation und erzeugt eindeutige Bezeichner für Verknüpfungen.

Weitere Unterschiede

Es gibt einige weitere Unterschiede:

gp  Der >>>-Operator ist in C# nicht vorhanden, da der >>-Operator ein verschiedenes Verhalten für Typen mit und ohne Vorzeichen aufweist.
gp  Anstelle von instanceof wird der is-Operator verwendet.
gp  Es ist keine benannte break-Anweisung vorhanden, diese wird durch goto ersetzt.
gp  Die switch-Anweisung verbietet das »Durchfallen« von Code, switch kann für Zeichenfolgenvariablen eingesetzt werden.
gp  Es ist nur eine Arraydeklarationssyntax verfügar: int[] arr.
gp  C# ermöglicht bei Verwendung des params-Schlüsselwortes eine variable Anzahl Parameter.

Galileo Computing

35.3 Unterschiede zwischen C# und Visual Basic 6  downtop

C# und Visual Basic 6 sind relativ unterschiedliche Sprachen. C# ist eine objektorientierte Sprache, Visual Basic 6 bietet nur beschränkte objektorientierte Funktionen. VB7 weist zusätzliche objektorientierte Funktionen auf, daher kann eine Lektüre der VB7-Dokumentation sehr aufschlussreich sein.


Galileo Computing

35.4 Codeaussehen  downtop

In Visual Basic enden Anweisungsblöcke auf eine Art von END-Anweisung, und es dürfen sich nicht mehrere Anweisungen in einer Zeile befinden. In C# werden Blöcke mit geschweiften Klammern ({}) gekennzeichnet, und die Position der Zeilenumbrüche spielt keine Rolle, da das Ende einer Anweisung mit einem Semikolon gekennzeichnet wird. Obwohl der nachfolgende Code vielleicht keinen guten Stil darstellt und hässlich zu lesen ist, ist er in C# möglich:

for (int j = 0; j < 10; j++) {if (j == 5) Func(j); 
else return;}

Diese Zeile trägt die gleiche Bedeutung wie der folgende Codeabschnitt:

for (int j = 0; j < 10; j++)
{
    if (j == 5)
        Func(j);
    else
        return;
}

Auf diese Weise wird der Programmierer zwar weniger eingeschränkt, es sind jedoch auch Abkommen bezüglich des Stils erforderlich.


Galileo Computing

35.4.1 Datentypen und Variablen  downtop

Obwohl viele der in VB und C# verwendeten Datentypen übereinstimmen, gibt es einige wichtige Unterschiede, ein ähnlicher Name kann beispielsweise einen anderen Datentyp bezeichnen.

Der wichtigste Unterschied besteht darin, dass C# bei Variablendeklaration und -verwendung strikter ist. Alle Variablen müssen vor der Verwendung deklariert werden, und sie müssen mit einem bestimmten Typ deklariert werden – es ist kein Variant-Typ vorhanden, der einen beliebigen Typ enthalten kann.

Variablendeklarationen erfolgen einfach durch Einfügen des Typnamens vor der Variablen; es gibt keine dim-Anweisung.

Konvertierungen

Konvertierungen zwischen Typen werden in C# ebenfalls strenger gehandhabt als in Visual Basic. C# kennt zwei Arten der Konvertierung, die implizite und die explizite Konvertierung. Implizite Konvertierungen sind diejenigen, bei denen kein Datenverlust auftritt – d. h., der Quellwert passt stets in die Zielvariable. Beispiel:

int    v = 55;
long x = v;

Das Zuweisen von v zu x ist möglich, da int-Variablen immer in long-Variablen passen.

Explizite Konvertierungen dagegen sind Konvertierungen, bei denen ein Datenverlust auftreten kann bzw. die fehlschlagen können. Aufgrund dieser Tatsache muss die Konvertierung mit Hilfe einer Typumwandlung explizit angegeben werden:

long    x = 55;
int v = (int) x;

Obwohl die Konvertierung in diesem Fall sicher ist, kann long Zahlen enthalten, die zu groß sind, um in einen int-Wert zu passen, daher ist eine Typumwandlung erforderlich.

Wenn das Ermitteln eines Überlaufs während der Konvertierung von Bedeutung ist, kann mit der checked-Anweisung die Überlaufermittlung aktiviert werden. Weitere Informationen hierzu finden Sie in Kapitel 15, Konvertierungen.

Datentypenunterschiede

In Visual Basic lauten die ganzzahligen Datentypen Integer und Long. In C# werden diese durch die Typen short und int ersetzt. Es ist ebenfalls ein long-Typ vorhanden, bei diesem handelt es sich jedoch um einen 64-Bit-Typ (8-Byte). Dies sollten Sie im Hinterkopf behalten, denn wenn Sie in C# dort Long einsetzen, wo Sie in Visual Basic long verwenden, werden die Programme sehr viel größer und langsamer. Byte dagegen kann fast mit byte gleichgesetzt werden.

C# verfügt des Weiteren über die Datentypen ushort, uint und ulong (ohne Vorzeichen) sowie den Typ sbyte, einen byte-Wert mit Vorzeichen. Diese sind in bestimmten Situationen nützlich, funktionieren jedoch nicht in allen weiteren .NET-Sprachen und sollten daher sparsam eingesetzt werden.

Die Gleitkommatypen Single und Double werden in float und double umbenannt, und der Boolean-Typ wird zu bool.

Zeichenfolgen

Viele der integrierten Funktionen von Visual Basic sind für C#-Zeichenfolgentypen nicht verfügbar. Es gibt Funktionen für das Suchen von Zeichenfolgen, das Extrahieren von Teilzeichenfolgen und das Durchführen weiterer Operationen. Siehe hierzu die Dokumentation des System.String-Typs.

Die Zeichenfolgenverkettung erfolgt nicht über den &-Operator, sondern über +.

Arrays

In C# erhält das erste Element eines Arrays immer den Index 0, und es gibt keine Möglichkeit, höhere oder niedrigere Grenzen festzulegen oder ein redim für Arrays auszuführen. Über ArrayList im Namespace System.Collection wird jedoch eine Dimensionierung bereitgestellt, zusammen mit weiteren nützlichen Auflistungsklassen.


Galileo Computing

35.4.2 Operatoren und Ausdrücke  downtop

Die C#-Operatoren weisen gegenüber Visual Basic einige wenige Unterschiede auf, daher müssen Sie sich mit diesen besonders vertraut machen.

VB-Operator C#-Äquivalent
^ Keiner. Siehe Math.Pow()
Mod %
& +
= ==
<> !=
Like Keiner. System.Text.RegularExpressions.Regex erledigt einige dieser Aufgaben, ist jedoch komplexer.
Is Keiner. Der C#-Operator is trägt eine andere Bedeutung.
And &&
Or ||
Xor ^
Eqv Keiner. A Eqv B entspricht !(A ^ B).
Imp Keiner.


Galileo Computing

35.4.3 Klassen, Typen, Funktionen und Schnittstellen  downtop

Da C# eine objektorientierte Sprache ist, stellen Klassen die hauptsächliche Organisationseinheit dar; Code oder Variablen werden nicht in globalen Bereichen verwaltet, sondern immer mit einer spezifischen Klasse verknüpft. Dies führt zu Code, der recht anders als der Visual Basic-Code strukturiert und organisiert wird. Dennoch gibt es weiterhin Gemeinsamkeiten. Eigenschaften können weiterhin verwendet werden, auch wenn sie eine andere Syntax aufweisen und keine Standardeigenschaften vorhanden sind.

Funktionen

In C# müssen Funktionsparameter einen deklarierten Typ aufweisen, und anstelle von ByVal wird mit Hilfe von ref angegeben, dass der Wert einer übergebenen Variable bearbeitet werden kann. Die Funktion ParamArray kann durch Verwenden des params-Schlüsselwortes ausgeführt werden.


Galileo Computing

35.4.4 Steuerung und Programmfluss  downtop

C# und Visual Basic verfügen über ähnliche Steuerungsstrukturen, die verwendete Syntax unterscheidet sich jedoch leicht.

If Then

In C# gibt es keine Then-Anweisung; nach der Bedingung folgt die Anweisung oder der Anweisungsblock, die/der bei erfüllter Bedingung ausgeführt werden soll. Im Anschluss an Anweisung oder Anweisungsblock kann eine optionale else-Anweisung vorliegen.

Der folgende Visual Basic-Code

If size < 60 Then
    value = 50
Else
    value = 55
    order = 12
End If

kann umgeschrieben werden zu

if (size < 60)
    value = 50;
else
{
    value = 55;
    order = 12;
}

In C# gibt es keine ElseIf-Anweisung.

For

Die Syntax von for-Schleifen ist in C# anders, das Konzept bleibt jedoch dasselbe, abgesehen davon, dass die am Ende einer Schleife durchgeführte Operation in C# explizit angegeben werden muss. Mit anderen Worten, der folgende Visual Basic-Code

For i = 1 To 100
    ' Weiterer Code hier
}
kann umgeschrieben werden zu
for (int i = 0; i < 10; i++)
{
    // Weiterer Code hier
}

For Each

C# unterstützt die For Each-Syntax über die foreach-Anweisung, die für Arrays, Auflistungsklassen und weitere Klassen eingesetzt werden kann, die eine geeignete Schnittstelle offen legen.

Do Loop

C# weist zwei Schleifenkonstruktionen aus, die das Do Loop ersetzen. Die while-Anweisung wird zum Durchlaufen einer Schleife verwendet, während eine Bedingung erfüllt ist, do while arbeitet auf die gleiche Weise, abgesehen davon, dass auch ein Schleifendurchlauf vollzogen wird, wenn die Bedingung nicht erfüllt ist. Der folgende Visual Basic-Code

I = 1
fact = 1
Do While I <= n
    fact = fact * I
    I = I + 1
Loop

kann folgendermaßen umgeschrieben werden:

int I = 1;
int fact = 1;
while (I <= n)
{
    fact = fact * I;
    I++;
}

Eine Schleife kann mit Hilfe der break-Anweisung verlassen oder im nächsten Durchlauf mit der continue-Anweisung fortgesetzt werden.


Galileo Computing

35.4.5 Select Case  downtop

Die switch-Anweisung in C# führt die gleiche Aufgabe aus wie Select Case. Dieser VB-Code

Select Case x
    Case 1
        Func1
    Case 2
        Func2
    Case 3
        Func2
    Case Else
        Func3
End Select

kann folgendermaßen umgeschrieben werden:

switch (x)
{
    case 1:
        Func1();
        break;
    case 2:
    case 3:
        Func2();
        break;
    default:
        Func3();
        break;
}

Galileo Computing

35.4.6 On Error  downtop

In C# gibt es keine On Error-Anweisung. Fehlerbedingungen in .NET werden über Ausnahmen gehandhabt. Weitere Informationen zu diesem Thema finden Sie in Kapitel 4, Ausnahmebehandlung.


Galileo Computing

35.4.7 Fehlende Anweisungen  downtop

In C# sind weder With, Choose noch ein Äquivalent zu Switch verfügbar. Des Weiteren kann auch nicht auf die CallByName-Funktion zurückgegriffen werden, auch wenn diese Operation über die Reflektion ausgeführt werden kann.


Galileo Computing

35.5 Weitere .NET-Sprachen  toptop

Visual C++ und Visual Basic wurden beide zur Verwendung in der .NET-Welt erweitert.

In der Visual C++-Welt wurde der Sprache ein Satz »verwalteter Erweiterungen« hinzugefügt, um den Programmierern das Erzeugen und Verwenden von Komponenten für die Common Language Runtime zu ermöglichen. Das Visual C++-Modell stattet den Programmierer mit umfangreicheren Steuerungsmöglichkeiten aus als das C#-Modell, da sowohl verwaltete (Speicherbereinigung) als auch nicht verwaltete Objekte (new und delete) geschrieben werden können.

Eine .NET-Komponente wird durch das Verwenden von Schlüsselworten zum Bearbeiten der Bedeutung vorhandener C++-Konstruktionen erstellt. Wenn beispielsweise das Schlüsselwort __gc einer Klassendefinition vorangestellt wird, ermöglicht dieses Vorgehen die Erstellung einer verwalteten Klasse und verbietet der Klasse die Verwendung von Konstruktionen, die in der .NET-Umgebung nicht ausgedrückt werden können (beispielsweise die Mehrfachvererbung). Von den verwalteten Erweiterungen aus können auch die .NET-Systemklassen verwendet werden.

Visual Basic hat ebenfalls erhebliche Verbesserungen erfahren. Es werden nun objektorientierte Konzepte wie Vererbung, Kapselung und Überladung unterstützt, damit eine nahtlose Integration in die .NET-Umgebung gewährleistet ist.






1    Der Typ object kann einen beliebigen Typ enthalten, es ist jedoch bekannt, welcher Typ enthalten ist.

2    Siehe hierzu Kapitel 1, Grundlagen der objektorientierten Programmierung.

   

Select * from SQL Server 2000




Copyright © Galileo Press GmbH 2001 - 2002
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.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de