Berechnungen mit ganzzahligen numerischen Datentypen in Visual Basic .NET und C#

Einleitung

Vielfach wird bei Vergleichen der Programmiersprachen Visual Basic .NET und C# auf das Fehlen von Operatoren für die nicht CLS-konformen vorzeichenlosen ganzzahligen Datentypen in Visual Basic .NET hingewiesen und dies als Argument gegen den Einsatz von Visual Basic .NET gewertet. Weitaus seltener findet in derartigen Gegenüberstellungen Erwähnung, daß in C# Rechenoperationen für die Datentypen UInt8, UInt16, Int8 und Int16 immer mit dem Datentyp Int32 durchführt und deshalb zusätzliche Typumwandlungen erforderlich werden. Ziel dieses Artikels ist es, die Gründe für dieses Verhalten von C# zu beschreiben und den Umgang von Visual Basic .NET mit den genannten Datentypen zu untersuchen. Des Weiteren erfolgt eine Bewertung der Unterstützung von Berechnungen mit ganzzahligen numerischen Datentypen in den beiden Programmiersprachen.

Die folgenden Ausführungen beziehen sich auf die Programmiersprachen Visual Basic .NET 7.1 mit Option Strict On und C# 1.1, beide mit aktivierter Überprüfung auf Ganzzahlüberlauf, sowie das .NET Framework 1.1. Um den Text verständlich zu halten, werden anstelle der sprachspezifischen Typpseudonyme die allgemein bekannten Namen der Typen aus dem .NET Framework verwendet. Die nachstehende Tabelle bietet einen Übersicht über die Namen der Typen im .NET Framework und den Programmiersprachen Visual Basic .NET und C#.

Namen ganzzahliger numerischer Datentypen im .NET Framework, Visual Basic .NET und C#
.NET Framework Visual Basic .NET C#
UInt8 Byte byte
UInt16 ushort
Int16 Short short
UInt32 uint
Int32 Integer int
UInt64 ulong
Int64 Long long

Operationen auf ganzzahlige numerische Datentypen in .NET

In MSIL sind die Anweisungen add, div, mul, rem und sub zur Durchführung binärer Operationen auf Ganzzahlen für die Typen Int32 und Int64 ([1], Teil III CIL Instruction Set, Kap. 1.5 Operand Type Table, Tab. 2 Binary Numeric Operations, S. 10), nicht aber für UInt8, UInt16 und Int16 definiert. Die Entscheidung dafür mag damit begründet werden, daß in der Praxis mit aktuellen Prozessoren und aufgrund der Wertebereiche der Datentypen die meisten Berechnungen mit den Datentypen der Bitbreiten 32 und 64 durchgeführt werden und Berechnungen mit Datentypen geringerer Bitbreite eher eine Seltenheit darstellen. Zudem können Berechnungen für Datentypen geringerer Bitbreiten problemlos mit den Anweisungen für 32- bzw. 64-Bit-Datentypen simuliert werden. Dazu werden die Operanden beispielsweise in den Datentyp Int32 konvertiert, anschließend mit den dafür definierten Anweisungen die Berechnungen durchgeführt und das Ergebnis in den Zieldatentyp umgewandelt.

Bei Berechnung der Summe zweier Zahlen des Typs Int16 und Zuweisen des Ergebnisses an eine Variable dieses Typs kann selbst dann kein Genauigkeitsverlust auftreten, wenn die Berechnungen unter Verwendung des Datentyps Int32 durchgeführt werden. Möglich ist jedoch, daß beim Umwandeln des Ergebnisses in den Zieldatentyp ein Überlauf eintritt, falls der Wertebereich des Zieldatentyps kleiner ist als jener des Datentyps, mit dem die Berechnung durchgeführt wurde, oder nicht vollständig mit diesem übereinstimmt. Ein Überlauf in Folge von Berechnungen kann selbst dann auftreten, wenn Zielvariable und Berechnung den selben Datentyp besitzen.

Ganzzahlige Berechnungen in Visual Basic .NET

In Visual Basic .NET werden arithmetische Operatoren für die ganzzahligen numerischen Datentypen UInt8, Int16, Int32 und Int64 unterstützt (vgl. [2], Kap. 11.13.3 Addition Operator). Bei Datentypen wie UInt8 und Int16, für die in der CIL keine entsprechenden Anweisungen enthalten sind, werden die Berechnungen mit den Anweisungen für den Datentyp Int32 unter Wahrung der Typsicherheit auf Ebene des Quellcodes simuliert. Die Addition von a und b erfolgt mittels der Anweisung add, welche die beiden Zahlen als Int32 behandelt und keine Überlaufüberprüfung vorsieht. Eine Überprüfung auf einen Überlauf ist nicht erforderlich, da das Ergebnis einer Addition zweier Zahlen des Typs Int16 immer im Datentyp Int32 dargestellt werden kann. Anschließend wird das Ergebnis der Addition mit conv.ovf.i2 in den Datentyp Int16 umgewandelt. Tritt bei der Umwandlung ein Überlauf auf, wird eine Ausnahme des Typs System.OverflowException geworfen.

Dim a As Int16 = 22
Dim b As Int16 = 12
Dim c As Int16 = a + b

Hierbei ist bemerkenswert, daß die Umwandlung in den Typ Int16 unabhängig vom Typ der Variablen, welcher das Berechnungsergebnis zugewiesen wird, erfolgt. Hätte die Zielvariable den Datentyp UInt8, würde der Compiler zwei aufeinanderfolgende Anweisungen zur Typumwandlungen emittieren; eine conv.ovf.i2-Anweisung zum Zweck der Umwandlung des Berechnungsergebnisses in den Datentyp Int16 und eine conv.ovf.u1-Anweisung, die dazu dient, den Wert in den Datentyp der Zielvariablen umzuwandeln. Bei fehlender Optimierung seitens des Compilers könnten daher in unserem Beispiel zwei direkt aufeinanderfolgende conv.ovf.i2-Anweisungen emittiert werden, von denen eine redundant wäre.

Mit strenger Semantik erlaubt Visual Basic .NET nur erweiternde, nicht jedoch reduzierende implizite Typumwandlungen ([2], Kap. 8.1 Implicit and Explicit Conversions und Kap. 6.2.2 Option Strict Statement). Bei der Addition zweier Zahlen besitzt, vereinfacht ausgedrückt, das Ergebnis den Typ jenes der beiden Operanden, der einen größeren Wertebereich aufweist. Operanden anderer Typen werden implizit in diesen Typ umgewandelt, sofern es sich dabei um eine erweiternde Umwandlung handelt, andernfalls wäre eine explizite reduzierende Umwandlung erforderlich.

Ganzzahlige Berechnungen in C#

C# stellt keine arithmetischen Operatoren für die Datentypen UInt8, UInt16 und Int16 bereit (vgl. [3], Kap. 7.7.4 Addition operator) und implementiert damit Operationen lediglich für die in der CIL unterstützten Datentypen, d. h. jene Typen, für die in der MSIL entsprechende Anweisungen bereitstehen. Der im folgenden Listing angegebene Code, in dem die Werte der zwei Variablen a und b addiert und die berechnete Summe der Variablen c zugewiesen werden soll, führt zum Kompilierungsfehler CS0029 (Implizite Konvertierung des Typs int zu short nicht möglich). Um den Code kompilierbar zu machen, muß das Ergebnis der Berechnung a + b explizit in den Typ der Zielvariablen umgewandelt werden (c = (Int16)(a + b)). Zur Durchführung der Addition wird die IL-Anweisung add.ovf verwendet, die bei Auftreten eines Überlaufs eine Ausnahme des Typs System.OverflowException wirft. Die Typumwandlung des Additionsergebnisses erfolgt mittels der Anweisung conv.ovf.i2, von der ebenfalls im Falle eines Überlaufs eine System.OverflowException geworfen wird.

Int16 a = 22, b = 12, c = a + b;

Mit der zusätzlichen expliziten Typumwandlung vor Zuweisen des Ergebnisses der Berechnung ähnelt das Verhalten jenem von Visual Basic .NET. Allerdings wird der Codierungsaufwand erhöht, ohne zur Kompilierungszeit oder Laufzeit einen Nutzen zu bringen. Da der Typ der Variablen, die das Berechnungsergebnis aufnehmen soll, zur Kompilierungszeit bekannt ist, kann die Typumwandlung in den Typ des Zuweisungsziels automatisch generiert werden. Eine Erhöhung der Qualität des Quellcodes durch das Erzwingen einer expliziten Typumwandlung kann gleichfalls nicht konstatiert werden, da eine derartige Vorgangsweise bei den Typen Int32 und Int64 auch nicht verpflichtend ist.

Schlußwort

Die Programmiersprachen Visual Basic .NET und C# unterscheiden sich in ihrer Unterstützung für die Durchführung mathematischer Operationen auf Werte ganzzahliger numerischer Datentypen. Ideal wäre eine vollständige typsichere Unterstützung der Operatoren für alle ganzzahligen numerischen Datentypen in Visual Basic .NET und C#. Visual Basic .NET 8.0 erweitert die Programmiersprache um Operatoren für die bisher noch nicht unterstützten vorzeichenlosen numerischen Datentypen. Andererseits ist in C# 2.0 keine mit jener von Visual Basic .NET vergleichbare typsichere Unterstützung von Berechnungen bislang fehlender numerischer Datentypen vorgesehen, weshalb Visual Basic .NET seine Vorzüge gegenüber C#, insbesondere in Interop-Szenarien, weiter ausbauen kann.

Literaturverzeichnis

[1]
Microsoft Corporation: Standard ECMA-335 – Common Language Infrastructure (CLI), 2nd edition, ECMA, Dezember 2002.
[2]
Microsoft Corporation: Visual Basic Language Specification, Dokumentation zu Visual Basic .NET 2003, MSDN, 2003.
[3]
Microsoft Corporation: Standard ECMA-334 – C# Language Specification, 2nd edition, ECMA, Dezember 2002.

Weiterführende Informationen

Dave Doknjas: Convert Between VB.NET and C#

Unterschiede zwischen Visual Basic .NET und C# hinsichtlich ihrer Typsicherheit.