C# und VB.Net gemeinsam nutzen

25.08.2006 von Rene Martin
C# und Visual Basic.Net lassen sich beliebig in Projekten mischen, so dass man im Netz gefundene Klassen schnell in eigene Programme einbauen kann. Ein gewisses Verständnis für die Unterschiede ist allerdings hilfreich.

Es spielt keine Rolle, in welcher Programmiersprache Sie in Visual Studio .Net ein Programm oder eine Klasse schreiben. Die kompilierte Datei, die von der Common Language Runtime ausgeführt wird, sieht – wenn man den Bytecode betrachtet – exakt gleich aus unabhängig von der Sprache, in der er entwickelt wurde.

Von der großen Sprachenvielfalt, die es inzwischen unter .Net gibt, haben sich in den letzten Jahren C# und VB.Net als die beiden beliebtesten Sprachen herauskristallisiert. C# sicherlich deshalb, weil die Syntax, der Aufbau, die Organisation und die dahinter stehende Philosophie Java sehr ähnlich sind und recht nah an C++ heranreichen. Nicht von ungefähr wurde die Notation C# gewählt, die in der Musik nichts bedeutet, als die Erhöhung der Note C um eine halbe Note (cis). Das soll sicherlich implizieren, dass C# noch eine Spur besser ist als C oder C++. Umgekehrt ähnelt die Syntax von Visual Basic.Net sehr der von Visual Basic, beziehungsweise Visual Basic for Applications, womit Programmierern dieser beliebten Sprachen der Einstieg erleichtert werden soll.

Oder gibt es nicht sogar Konstrukte, die in VB.Net bestimmte Codeteile leichter programmieren lassen als in C#? Mit welchen Schwierigkeiten haben versierte C#-Programmierer zu kämpfen, die nun in ein VB.Net-Projekt einsteigen müssen? Oder umgekehrt. Immerhin bieten Plattformen wie gotdotnet.com oder andere Open-Source-Projekte fertige Klassen für eine Vielzahl von Anwendungsbereichen an. Es bietet sich also an, das Rad nicht ständig neu zu erfinden. Um den jeweils anderen Code allerdings verstehen zu können, benötigt man zumindest eine grundlegende Idee von der Philosophie und der Struktur der anderen Sprache.

Gibt es doch Unterschiede?

Augenfälligster Unterschied zwischen beiden Sprachen ist, dass VB.Net (in guter Basic-Tradition) eine Anweisung als eine Zeile schreibt, während C# das Semikolon als Zeilenbegrenzungszeichen verwendet. Beide Sprachen können jedoch Zeichenketten nicht auf mehrere Zeilen verteilen. In VB.Net führt

MessageBox.Show("Dieses Programm wurde _
für die Firma xyz von René Martin erstellt")

ebenso wie

MessageBox.Show("Dieses Programm wurde
für die Firma xyz von René Martin erstellt");

in C# zu einem Fehler. VB-Programmierer haben häufig ohnehin Schwierigkeiten mit dem abschließenden Strichpunkt, da der Befehl

If i = 0 Then MessageBox.Show("Wert ist zu klein")

in C# wie folgt programmiert wird

if (intMonat == 0)
MessageBox.Show("Wert ist zu klein");

Mehrere Zeilen im If-Zweig

Will man in C# zwei Befehlszeilen in den True-Zweig einbauen, dann darf nicht geschrieben werden:

if (intMonat == 0)
MessageBox.Show("Wert ist zu klein");
MessageBox.Show("Bitte ändern Sie dies!");

sondern man muss eine geschweifte Klammer setzen. Sie sollte generell gesetzt werden, um Fehler zu vermeiden. Auch in VB.Net sollten Sie besser von einer einzeiligen Anweisung

If i = 0 Then MessageBox.Show("Wert ist zu klein")

Abstand nehmen. Das ist allerdings insoweit nicht schwer, da die Entwicklungsumgebung nach der Eingabe von If i = 0 den Rest automatisch komplettiert, nämlich

If i = 0 Then
...
End If

Die geschweiften Klammern werden in C# leider nicht automatisch vervollständigt.

Do und While

Nicht ganz konsistent erscheint für VB-Programmierer das Setzen oder Nichtsetzen des Semikolons in der while-Schleife von C#:

while (i < 12) {
i++;
}

Dagegen verlangt die fußgesteuerte Schleife ein abschließendes Semikolon:

do {
i++;
} while (i < 12);

Und: Anders als VB.Net kennt C# kein Until, das zugegeben überflüssig ist, da normalerweise der Vergleichsoperator umgedreht werden kann:

Do While i < 12
i++
Loop

entspricht von der Programmierlogik her

Do Until i >= 12
i++
Loop

Kommentare und andere Hürden

Die Tatsache, dass VB.Net keine mehrzeiligen Kommentare erlaubt, kann sicherlich verschmerzt werden – dafür stellt die Entwicklungsumgebung für beide Sprachen jeweils ein Symbol zum Ein- und Auskommentieren zur Verfügung.

Großkleinschreibung und andere Hürden beim Tippen

VB.Net unterscheidet (wie alle Basic-Dialekte) nicht zwischen Groß- und Kleinschreibung. Die IDE besitzt sogar die angenehme Eigenschaft, dass sie die Schreibweise bei der Eingabe automatisch anpasst. Deklarieren Sie beispielsweise:

Dim intMonat As Integer

Dann wandelt die IDE ein getipptes „intmonat“ im Code sofort zu „intMonat“ um, was die lästigen Tippfehler schnell sichtbar macht. In C# muss dagegen „intMonat“ korrekt getippt werden.

Jedoch stellt die Entwicklungsumgebung seit der Version 2.0 die angenehme Eigenschaft zur Verfügung, dass IntelliSense beim Schreiben von „int“ gleich die Auflistung sämtlicher Members anzeigt, die mit „int“ beginnen. Umgekehrt: Bei Korrekturen kann man die Tastenkombination <Strg>+<Leertaste> drücken. Intellisense zeigt dann die Möglichkeiten an oder vervollständigt den Begriff automatisch, falls der begonnene Wortname eindeutig ist.

Jedoch hat die Groß- und Kleinschreibung noch einen weiteren, nicht zu vernachlässigenden Aspekt: Da sich Klassen in unterschiedlichen Sprachen erstellen lassen, können Sie in einer Klasse in C# sowohl die Methode „konto“ als auch „Konto“ (oder gar: „KONTO“) implementieren. Wenn Sie diese Klasse mit ihren Methode dann in VB.Net verwenden wollen, so steht VB.Net vor einem Problem. Die Lösung: Sie sollten auf so etwas komplett verzichten! Denn auch, wenn Sie nur in C# programmieren, kann das schnell zu Fehlern führen.

Variablen

Eine der unangenehmen Eigenschaften, die VB.Net von VB geerbt hat, sind Option Explicit, glücklicherweise in der Version 2.0 als Standard auf On gesetzt, und Option Strict, leider immer noch auf Off in Extras / Optionen eingestellt.

Wird erstere auf Off gesetzt, dann müssen Variablen nicht deklariert werden, bleibt letztere auf Off, dann sind folgende Zeilen korrekt:

Dim i As Integer = 42
Dim strAusgabe As String
strAusgabe = "Die Lösung lautet " & i
MessageBox.Show(strAusgabe)
strAusgabe = "104"
i = strAusgabe
MessageBox.Show(i)

Von solchem impliziten Typkonvertierungen sollten Sie jedoch Abstand nehmen, da der Code fehleranfälliger wird und das Programm leichter abstürzen kann.

Beide Programme können Variablen an Methoden als Referenz oder als Wert übergeben. Jedoch bietet nur C# die Möglichkeit, nicht initialisierte Variablen mit out statt mit ref zu übergeben. So verlangt zwar die Methode TryParse() einen out-Parameter – aber er muss nicht festgelegt werden.

Strings

Will man Sonderzeichen in C# ausgeben, dann stehen die Escape-Sequenzen zur Verfügung, beispielsweise „\n“ für den Zeilenumbruch. In VB.Net müssen Sie für denselben Effekt umständlich verketten. Alle drei Zeilen erzielen dasselbe Ergebnis:

strMessage = "Hallo" & vbCR & "VB.Net"
strMessage = "Hallo" & System.Environment.NewLine & "VB.Net"
strMessage = "Hallo" & Chr(13) & "VB.Net"

Umgekehrt bedeutet dies beim Verarbeiten einer Pfadangabe in C#, dass Sie beispielsweise bei folgender Zuweisung Probleme bekommen:

strDatei = "c:\test.txt";

Das „\t“ würde C# nämlich in einen Tabulator umwandeln. Es gibt zwei Möglichkeiten, das zu umgehen. Entweder „escapen“ Sie das Backslash durch Verdoppelung „\\“:

strDatei = "c:\\test.txt";

oder Sie verhindern durch ein vorangestelltes „@“, dass C# den String parst:

strDatei = @"c:\test.txt";

Arrays

Aus Bequemlichkeit speichern VB-Programmierer Variablen gleichen Typs gerne in Arrays, da sich diese bei Bedarf leicht per ReDim Preserve vergrößern lassen. Zudem ist das Handling von Arrays sehr einfach.

Dim arrStrings() as String
Dim i as Integer

For i = 1 To 5000
ReDim Preserve arrStrings(i)
arrStrings(i-1) = i
Next

Intern werden die Inhalte jedoch in ein neu erzeugtes Array kopiert, was auch schon bei wenigen Dutzend Datensätzen zum Performancekiller mutieren kann. Dies wirkt sich insbesondere auf IIS-Webservern mit .Net negativ aus, denn hier werden solche Routinen nicht nur wenige Male sondern unter Umständen mehrere hundert Mal pro Minute aufgerufen.

In C# existiert von vornherein kein ReDim bei Arrays – die Größe wird einmal definiert und bleibt dann so. Zwar lassen sich die Inhalte in andere Arrays kopieren, das ist aber beispielsweise beim dynamischen Vergrößern in Schleifen unpraktikabel. Deshalb verwenden C#-Programmierer von vornherein lieber Collections, Hashtables oder seit der Version 2.0 die so genannten Generics. Um ein ReDim unter C# nachzubilden, müsste man folgenden Code anwenden. Dieser verdeutlicht auch, wie das VB-Konstrukt intern umgesetzt wird.

string[] arrStrings = new string[1];
int i;

for (i=1;i<=5000;i++) {
string[] arrHelper = new string[i];
arrStrings.CopyTo(arrHelper, 0);
arrHelper[i-1] = i.ToString();
arrStrings = arrHelper;
}

Vergleichsoperatoren

Deutliche Unterschiede zwischen VB.Net und C# bestehen auch bei den Vergleichsoperatoren. In beiden Sprachen erfolgen Zuweisungen über das einfache Gleichheitszeichen, bei Vergleichen muss jedoch geschrieben werden:

if (i == 0)
if (i != 0)

If i = 0 Then
If i <> 0 Then

Zusätzlich stellt C# als logische Operatoren nicht nur „&“ und „|“ für „And“ beziehungsweise „Or“ zur Verfügung, sondern auch den bedingten Operator „&&“ beziehungsweise „||“, der nur wenn nötig den zweiten Teil des Ausdrucks auswertet. Also beim bedingten And nur dann, wenn der erste Operand true ergibt, da nur in diesem Fall die gesamte Operation noch true ergeben kann. Analog wird beim bedingten Or der zweite Operand nicht ausgewertet, wenn der erste schon true ist, denn dann ist das Ergebnis ohnehin true.

Ein ähnliches Verhalten muss in VB.Net mit geschachtelten Verzweigungen nachgebildet werden, da eine solche shortcut evaluation nicht existiert.

Achtung bei „=“ und Boolean

Da „=“ in VB.Net sowohl für die Übergabe, als auch für den Vergleich dient, wird in folgendem Code

Dim k As Integer = 7
i = j = k

das zweite Gleichheitszeichen als Vergleichsoperator interpretiert – das Ergebnis für i lautet 0 (=False). In C# hingegeben werden die Werte „sauber“ von rechts nach links geschrieben – das Ergebnis von

k = 7;
i = j = k;

ist 7. Ob solch ein Code lesbar ist, sei allerdings dahingestellt.

Eigentlich entspricht der Datentyp Boolean keiner Zahl, was bedeutet, dass in C# mit boolschen Variablen nicht gerechnet werden kann:

int i = 3;
MessageBox.Show ((Int32)(i == 3))*500;

führt zu einem Fehler. Jedoch funktioniert die gleiche Anweisung in VB.Net:

Dim i As Integer = 3
MsgBox(CInt(i = 3) * 500)

und liefert mit dem internen Wert -1 für True als Ergebnis -500. Will man diese Funktionsweise dennoch in C# nachbilden, dann muss mit der Funktion Sign aus der Klasse Math gearbeitet werden.

Der überladene Operator „+“

Das Framework stellt den Operator „+“ für jeden Datentyp zur Verfügung. Sowohl in C# als auch in VB.Net können beispielsweise mit „+“ Texte verkettet werden. Es empfiehlt sich in VB.Net allerdings, den Operator „&“ zu verwenden. Und zwar sowohl aus Gründen der Lesbarkeit als auch um sicherzustellen, dass am Ende wirklich eine Zeichenkette herauskommt:

strPfad = "C:\"
strPfad &= "11" & "11" & "\"

liefert "C:\1111\", was bei

strPfad += "11" + "11" + "\"

nicht klar ist. Um es deutlich zu formulieren:

i = 11
strAus = i
strAus &= i

liefert 1111, dagegen lautet das Ergebnis 22 in

i = 11
strAus = i
strAus += i

obwohl die Variable strAus als String deklariert wurde.

Bei einer „großen“ Anzahl an Zeichenketten sollten Sie übrigens in beiden Sprachen die Klasse StringBuilder verwenden, da Strings sich zwar nach außen wie Wertetypen verhalten, allerdings intern als neue Objekte im Speicher angelegt werden. Und das kann die Performance belasten!

Verzweigungen: Select und Switch

Sicherlich kann man auf philosophischer Ebene darüber streiten, wann bei einer Verzweigung If und wann Select/Switch verwendet werden sollte. Jedoch gibt es bei dem Konstrukt Select/Switch einige kleine Unterschiede zwischen C# und VB.Net:

In VB.Net ist es möglich, mehrere Fälle in eine Select-Anweisung zu packen, mit Vergleichsoperatoren und mit Variablen zu arbeiten:

Select Case intMonat
Case 1, 2, 3
strQuartal = "erstes Quartal"
Case intM4, intM5, intM6
strQuartal = "zweites Quartal"
Case 7 To 9
strQuartal = "drittes Quartal"
Case Is > 9
strQuartal = "viertes Quartal"
End Select

In C# sind diese vier Optionen nicht möglich. Jedoch wird hier nicht jeder einzelne Zweig abgearbeitet, sondern es folgt sofort einen Sprung in den entsprechenden Teil:

switch (intMonat) {
case 1:
case 2:
case 3:
strMonat = "erstes Quartal"; break;
case 4:
case 5:
case 6:
strMonat = "zweites Quartal"; break;
case 7:
case 8:
case 9:
strMonat = "drittes Quartal"; break;
case 10:
case 11:
case 12:
strMonat = "viertes Quartal"; break;
}

benötigt, falls intMonat = 10 lediglich zwei Schritte. Ein klares Unterscheidungsmerkmal: Select sollte in VB.Net verwendet werden, wenn mehrere Fälle gleichzeitig abgefragt werden (um nicht mit OR verknüpfen zu müssen), bei vielen verschiedenen Anweisungen empfiehlt sich Switch in C# aus Geschwindigkeitsgründen.

Schleifen: For

In beiden Sprachen existiert die Schleife For, die sich allerdings nicht nur durch die Syntax unterscheidet. In VB.Net ist For-Next eine reine Zählschleife, bei der Startwert, Endwert und Inkrement (implizit oder explizit) angegeben werden müssen:

For i = 1 To 10
...
Next

Bei C# handelt es sich dagegen um Initialisierung, Abbruchbedingung und Iterator. Diese Angaben können partiell oder vollständig weggelassen werden. Statt

for (i = 1; i <= 10; i++) {

}

können Sie auch

for (i = 1; i <= 10;) {
...
i++;
}

programmieren oder gar

for (;;) {
...
}

Im letzten Fall hätten Sie eine Endlosschleife, die Sie durch ein break; abbrechen müssen. Damit können Sie über die for-Schleife in C# eine Bedingungsschleife nachbilden – sie kann die do ... while-Schleife ersetzen.

Umgekehrt fehlt das Until aus VB.Net in C# - auf das man allerdings verzichten kann, da man lediglich den Vergleichsoperator umkehren muss.

With als Klammer für Objekte

Ein angenehmes Konstrukt in VB.Net ist eine Klammerung mit With-End With:

With Klasse1
.Methode1()
.Methode2()
.Methode3()
.Eigenschaft4 = "ein Test"
End With

In C# fehlt eine solche Konstruktion, die nicht nur Schreibarbeit spart sondern auch etwas Performance bringt.

Funktionen

VB.Net unterstützt noch immer die Funktionen des Namespace Microsoft.VisualBasic. Zugegeben: Auf MsgBox kann man zugunsten von MessageBox.Show problemlos verzichten, statt Left oder Right können Sie Substring und statt InStr die Methode IndexOf verwenden. Jedoch ist vbCr einfach praktischer als System.Environment.NewLine oder auch die Funktion IsNumeric, die in reinem VB.Net mit Decimal.Parse oder Decimal.TryParse nachgebildet werden muss. Wollte man in C# diese Funktionen benutzen, könnte man eine Referenz auf die Assembly Microsoft.VisualBasic setzen, was jedoch nicht unbedingt empfehlenswert ist, da man so „aus dem System herausspringt“.

Zugriff auf MS Office oder VB

In VB und VBA in Microsoft Office existieren optionale Parameter, das heißt Methoden können Werte erhalten oder nicht. Da weder VBA noch VB „echt“ objektorientiert sind, besitzen sie keine überladenen Methoden. Das bedeutet beispielsweise, dass Sie in VBA statt

Set wdDokNeu = Documents.Add(Template:="", NewTemplate:=False, DocumentType:= wdNewBlankDocument, Visible:=True)

schreiben können

Set wdDokNeu = Documents.Add

In VB.Net können Sie mit wdDokNeu = wdApp.Documents.Add() arbeiten, in C# müssen Sie auf die Konstante System.Missing zurückgreifen:

objMiss = System.Missing
wdDokNeu = wdApp.Documents.Add(objMiss, objMiss, objMiss, objMiss)

was bei vielen Codezeilen schnell zu Fehlern führen kann.

Klassen

Klassen unterschieden sich in den beiden Sprachen nicht: Beide verfügen über Konstruktoren, Methoden, Eigenschaften, überladene Methoden und Eigenschaften (und seit 2.0 auch Operatoren), Polymorphie sowie versiegelte, verschachtelte und abstrakte Klassen. Jedoch gibt es einen interessanten Aspekt der Unterscheidung:

Da der Konstruktor in VB.Net mit dem Schlüsselwort New erzeugt wird, ist er unabhängig vom Namen der Klasse. Beim Umbenennen der Klasse entstehen keine Probleme. In C# dagegen trägt der Konstruktor den gleichen Namen wie die Klasse. Beim Ändern des Klassennamens muss also auch der Name des Konstruktors geändert werden.

Zusammenfassung

Dass VB.Net sich zu einer echt objektorientierten Sprache gemausert hat, die C# in nichts nachsteht, dürfte sich bereits herumgesprochen haben. Dass sie zwar an einigen Stellen noch ein paar unschöne Seiten vom alten Visual Basic mit sich herumschleppt (Option Explicit) ist sicherlich ärgerlich.

Vergleicht man allerdings die einzelnen Merkmale miteinander, wird schnell klar, dass vor allem die Entwicklungsumgebung von VB.Net einige angenehme Features zur Verfügung stellt, die in C# fehlen: automatische Konvertierung in Groß-/Kleinschreibung, automatische Klammersetzung bei Methoden, automatische Vervollständigung von If, For und Do ... Loop (wobei das Pendant der Klammersetzung in C# fehlt).

Dass alte Sprachelemente von VB/VBA mit übernommen wurden, kann als Vorteil oder Nachteil gesehen werden. Für „geringe“ Datenmengen ist das ReDim von Arrays eine einfache Technik und die Klammerung mittels With wird gerne aus Gründen der Lesbarkeit eingesetzt.

Auch der Zugriff auf Microsoft Office gestaltet sich wegen der Behandlung optionaler Parameter leichter durch VB.Net als durch C#.Leider haben aber auch unstrukturierte Konstrukte wie On Error Resume Next oder On Error GoTo Sprungmarke den Sprung von VB/VBA zu VB.Net geschafft.

Umgekehrt führen aber „fehlende“ Features zu einem anderen Programmierstil – wenn kein ReDim vorhanden ist, überlegt man sich schnell, ob man statt einem Array nicht besser eine Collection verwendet. Wenn kein Option Strict existiert, lernt man „sauberer“ zu programmieren und Switch wird in C# sicherlich anders eingesetzt als Select Case in VB.Net. (Rene Martin/mha)