Die Community zu .NET und Classic VB.
Menü

Untiefen in VB 2005

 von 

Übersicht 

Das Ziel dieser Kolumne ist es, wie der Name schon verrät, einige der Untiefen auszuloten, welche die letzten Neuerungen in .NET mit sich bringen. Hierbei beziehe ich mich sowohl auf VB als Sprache als auch auf das .NET Framework als Technologie, da sowohl syntaktische als auch semantische Unklarheiten aufgetaucht sind.

Der Artikel richtet sich primär an jene, welche sich bisher schon recht intensiv mit .NET befasst haben und nun einen Umstieg auf Version 2005 anvisieren.

Mit freundlichen Grüßen,
Konrad Ludwig Moritz Rudolph

Motivation  

Dieser Artikel soll dabei allerdings nicht als Abschreckung dienen, nicht einmal unbedingt als Orientierungshilfe. In gewisser Weise handelt es sich um einen offenen Brief an Microsoft, schließlich ist es an Microsoft, Fehlverhalten auszumerzen und nicht funktionierende Eigenschaften nachzuliefern. Die vorgestellten Fehlverhalten sind wahrscheinlich für einen relativ kleinen Prozentsatz an Entwicklern von primärer Bedeutung, leider jedoch gerade für solche, die Bibliotheken und deren öffentliche Schnittstellen entwickeln. Die Folge ist, dass es Bibliotheken geben wird, welche by design Schnittstellen besitzen werden, die sich nicht so verhalten, wie der Programmierer dies erwartet. Sie stellen somit eine gefährliche, da vom Compiler unerkannte und somit schwer zu entdeckende Fehlerquelle dar.

Implizite Typumwandlung  

Im folgenden werde ich mich auf eine Beispielklasse namens Complex stützen, welche ganz einfach eine komplexe Zahl repräsentieren soll. Dank Operatorenüberladung ist es nun endlich möglich, dass sich die Klasse Complex so intuitiv bedienen lässt wie eingebaute Datentypen.

Die Klasse (oder Struktur, die beiden werden im folgenden oft synonym gebraucht) besitzt zwei öffentliche Eigenschaften für den reelen und den imaginären Zahlenanteil, Re und Im. Außerdem verfügt sie über die gängigen Rechenoperationen Addition, Subtraktion etc.

Nun wollen wir uns den impliziten Typumwandlungsoperator zunutze machen, um die Klasse in Booleschen Ausdrücken nutzen zu können:

Partial Structure Complex
    Public Shared Widening Operator CType(ByVal rhs As Complex) As Boolean
        Return rhs.Re <> 0 OrElse rhs.Im <> 0
    End Operator
End Structure

Listing 1

Nun, dadurch ist folgender Code denkbar:

Option Strict On

Module Module1
    Sub Main()
        Dim i As New Complex(0, 1)
        If i Then MsgBox("i")
    End Sub
End Module

Listing 2

Der Code funktioniert dann auch wie erwartet - aber Moment! Spätestens bei der Zeile mit Option Strict On sollte man stutzig werden. Wenn wir statt Complex einen nativen Datentyp verwendet hätten, würde dieser Code nicht funktionieren: wir haben die implizite Typumwandlung nach Boolean doch mit dem Strict-Schalter ausgeschaltet.

Wir sollten uns noch einmal klar machen, wozu es Operatorenüberladung überhaupt gibt: Sie soll es uns ermöglichen, für eigene Datentypen das Verhalten nativer Datentypen nachzuahmen, um ihre Verwendung intuitiver (da einheitlich) zu gestalten.

Aus diesem Grund ist es auch durchaus logisch, eine implizite Typumwandlung zu definieren, denn mit Option Strict Off ist VB auch für native Datentypen in der Lage, solche Typumwandlungen durchzuführen. Der Schalter Strict regelt für uns gewissermaßen, inwiefern die Sprache selbständig wird. Wenn wir diesen Schalter aktivieren, erwarten wir, dass die Sprache weniger eigenständig wird. Leider ist dies, wie man am oberen Beispiel sieht, mit eigenen Datentypen nicht mehr der Fall - was den Strict-Schalter ad absurdum führt und den Programmierer vor ein Dilemma stellt: Soll er nun für eigene Datentypen in Bibliotheken implizite Typumwandlungen definieren (falls die Bibliothek mit Strict Off verwendet wird) oder soll er sie nicht definieren, damit man sie mit Strict On korrekt verwenden kann? Das aktuelle Verhalten ist ein Fehler. Egal, wie sich der Entwickler der Bibliothek entscheidet, für einen Teil der Benutzer wird die Schnittstelle der Bibliothek ein Fehlverhalten liefern.

Microsoft, bitte behebt dieses Verhalten in VB9!

Kurzschlussoperatoren  

Mit VB.NET kamen Boolesche Kurzschlussoperatoren. Eigentlich ein Grund zum Feiern, da diese Operatoren Code nicht nur effizienter sondern auf übersichtlicher gestalten können und übermäßiger Verschachtelung entgegenwirken. Der Grund hierfür ist, dass bei der Verwendung von Kurzschluss-And und Kurzschluss-Or der zweite Operator lediglich dann ausgewertet wird, wenn der Wert des Ausdrucks aus dem ersten Operator noch nicht eindeutig zu bestimmen ist:

Module Module2
    Sub Main()
        Dim c As Collection
        ' In VB6:
        If Not c Is Nothing Then
            If c.Count > 0 Then
                ' ...
            End If
        End If

        ' In VB.NET/2005:
        If c IsNot Nothing AndAlso c.Count > 0 Then
            ' ...
        End If
    End Sub
End Module

Listing 3

Wo man in VB6 noch zwei verschachtelte Abfragen benötigte, damit nicht aus Versehen auf ein nichtexistentes Objekt zugegriffen wird, reicht in VB.NET eine klare Abfrage. Man muss sich aber darüber im Klaren sein, dass es sich hierbei lediglich um "syntaktischen Zucker" handelt: Unter der Oberfläche tun diese beiden Codes dasselbe - was bedeutet, dass der resultierende Maschinencode beider Codes nahezu identisch ist und in einem Pseudo-Zwischencode so aussehen könnte:

if not c = nothing goto L1
goto Lfalse
L1:
if c.Count > 0 goto Ltrue
goto Lfalse
Ltrue: ; ...
Lfalse:

Listing 4

Hierbei ist Ltrue eine Marke, deren Code ausgeführt wird, wenn der If-Block betreten wird. Lfalse ist die Marke direkt hinter dem If-Block. Dieser Pseudocode ist per Definition das erwartete Verhalten bei einem Kurzschlussoperator.

Da es in VB 2005 die Möglichkeit gibt, für eigene Datentypen implizite Typumwandlungen nach Boolean bereitzustellen, sollte man erwarten, dass auch Kurzschlussoperatoren für eigene Datentypen funktionieren:

Module Module3
    Sub Main()
        Dim i As New Complex(0, 1)
        Dim j As New Complex(1, 0)
        If i AndAlso j Then
            ' ...
        End If
    End Sub
End Module

Listing 5

In unserem Pseudo-Zwischencode sähe dies dann (nun mit expliziter Typumwandlung, die vom Compiler eingefügt wird) so aus:

if i.to_bool goto L1
goto Lfalse
L1:
if j.to_bool goto Ltrue
goto Lfalse
Ltrue: ; ...
Lfalse:

Listing 6

Dies wäre das erwartete Verhalten. Allerdings zwingt Microsoft uns, für diesen Fall einen IsFalse-Operator zu implementieren, welcher dann im Prinzip die ersten drei Zeilen des Assemblercodes zu folgender macht:

if i.IsFalse goto Lfalse

Listing 7

Das ergibt sogar Sinn, denn dadurch haben wir einen Sprung und eine Sprungmarke gespart. Leider begnügt sich Microsoft nicht mit diesem kleinen Erfolg - der VB-Code funktioniert nämlich auch mit implementiertem IsFalse-Operator noch nicht. Vielmehr verlangt der Compiler, dass man auch sein Gegenstück, den IsTrue-Operator implementiert, welcher benötigt wird, wenn man OrElse verwendet. Hier beginnt das ganze, unsinnig zu werden, denn der IsTrue-Operator macht nichts anderes als eine Typumwandlung nach Boolean und ist somit absolut redundant.

Doch damit nicht genug, der IsFalse-Operator bewirkt in Wahrheit dasselbe wie der ebenfalls existierende Not-Operator: Noch mehr Redundanz. Natürlich nimmt einem der Compiler hier aber die Arbeit nicht ab: Man muss all diese Operatoren tatsächlich explizit definieren, möchte man sie verwenden. Von dem einen auf den anderen zu schließen, das würde dem Compiler nicht einfallen. Man könnte sogar die verschiedenen Operatoren auf unterschiedliche Weise definieren und somit semantische Inkonsistenzen einführen - eine gefährliche Fußfalle.

Und selbst hier musste Microsoft noch einen drauf geben, unser Code funktioniert noch immer nicht; der Compiler meckert, dass kein Operator And für zwei Complex-Parameter definiert sei. Wenn man sich unseren Pseudo-Zwischencode anschaut, sollte man stutzig werden: hier wird überhaupt kein And verwendet - wie auch? Der Sinn der Kurzschlussoperatoren war doch schließlich, das And zu umgehen. Was auch immer hier also geschieht, es ist nicht das erwartete Verhalten. Und diese Tatsache macht die Kurzschlussoperatoren für eigene Datentypen leider unbenutzbar.

Ich habe Microsoft von diesem Fehler über einen Betatest-Bericht in Kenntnis gesetzt und ihre Standardantwort erhalten, nämlich, dass man von dem Fehlverhalten Kenntnis genommen habe, dass jedoch im jetzigen Stadium der Entwicklung nur noch Fehler behoben würden, welche die Stabilität des Produkts einschränken.

Microsofts Philosophie scheint also zu sein: Hauptsache, ein Programm läuft stabil, ob es das Erwartete leistet, ist zweitrangig. Diese Einstellung ist für mich leider nicht nachvollziehbar. Microsoft liefert hier ein Produkt aus, welches über neue Funktionen verfügt (und damit wirbt), welche nicht funktionieren.

Download  

Der (nicht funktionierende) Testcode kann hier heruntergeladen werden:

Module1.vb [1763 Bytes]

Ihre Meinung  

Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.