Sprachpflege mit Roslyn: Neues in Visual Basic
Kommentare

Visual Basic zählte einst zu den populärsten Programmiersprachen der Welt. Im deutschsprachigen Raum kämpft VB heutzutage aber aus teils zweifelhaften Gründen und völlig zu Unrecht mit einem negativen Image, das dieser Sprache so gar nicht gerecht wird. Visual Basic ist vor allen Dingen alles andere als tot – es wird nicht nur in immer noch zahllosen kommerziellen Projekten eingesetzt, sondern auch sehr aktiv von Microsoft weiterentwickelt.

Anfang April ist Visual Basic als Teil des Projekts Roslyn, bei dem die Compiler in den und für die jeweiligen Sprachen C# und VB neu entwickelt wurden, zudem noch unter Open Source gestellt worden. Somit erhält es nicht nur viele neue Features, sondern wird auch auf anderen als der Windows-Plattform Einzug halten und sich zukünftig zur Cross-Plattformentwicklung ideal eignen.

Auf der Entwicklerkonferenz Build 2014 in San Francisco platzten kurz nacheinander zwei Bomben: Dass es nach fast zwei Jahren eigentlich eine neue Version der Roslyn-Compiler geben musste, war zwar eigentlich klar, doch wirklich damit gerechnet hatte zu diesem Zeitpunkt wahrscheinlich keiner, selbst der sonst stets wohlinformierten Insider. Die öffentliche Preview der Compiler war nicht nur unerwartet, sondern auch noch lange nicht alles: Anders Hejlsberg, Technical Fellow bei Microsoft und Vater von C#, gab sich die Ehre, live auf der Bühne das Roslyn-Projekt unter Open Source zu stellen.

Mit Roslyn gibt es ein komplettes Redesign der vorhandenen Visual-Basic- und C#-Compiler. Das Besondere daran ist, dass sie in sich selbst geschrieben sind, der C#-Compiler also komplett in C# und der Visual-Basic-Compiler komplett in Visual Basic. Das ermöglicht uns Entwicklern eine völlig neue Art und Weise zu coden, da es sich bei den Compilern nun nicht mehr um geheimnisvolle Blackboxen handelt, bei denen vorne Quellcode reingeht, mittendrin irgendein Voodoo-Zauber passiert, und hinten ausführbarer Code herauskommt. Oder Fehlermeldungen. Vielmehr haben wir Entwickler jetzt die Möglichkeit, sozusagen aktiv in den Parse- und Kompilierungsvorgang einzugreifen und die Compilervorgänge nahezu nach unserem Belieben zu steuern. Von automatischer Codedokumentation über Codegenerierung bis zu komplexer Codeanalyse oder Metaprogrammierung – um nur einige Beispiele zu nennen – ermöglicht es diese neue Compilertechnik also, extrem mächtige Tools zu entwickeln, die die Arbeit von Entwicklern – und ich denke nicht, dass diese Aussage übertrieben ist – in den kommenden Monaten und Jahren revolutionieren werden.

Dass beide Compiler nun unter Open Source verfügbar gemacht wurden, bedeutet obendrein, dass sie nicht mehr an die Microsoft-Plattform gebunden sind. In der Tat zeigte Cross-Plattformexperte Xamarin schon in einer ersten Demo auf der Build-Konferenz den auf den Mac portierten C#-Compiler.

Bei all der Aufregung und Spannung um diese Neuigkeiten ist aber auch eine Sache deutlich geworden: Die Sprache Visual Basic ist alles andere als tot. Microsoft hat ihr die gleiche „Zuneigung“ zuteilwerden lassen wie C#. Die Tatsache, dass ein dermaßen großes Investment in Visual Basic geflossen ist, zeigt nicht nur, dass Microsoft an einer kurzfristigen Weiterführung dieser Sprache interessiert ist. Vielmehr ist es mit Roslyn auch viel kostengünstiger geworden, Änderungen an der Sprache durchzuführen. Somit dürfen wir davon ausgehen, dass das Investment in Visual Basic ein langfristiges ist und dass uns die Sprache nicht nur noch sehr lange begleiten wird, sondern dass wir VB-Entwickler auch in Zukunft eine spürbare Evolution unserer Sprache erleben dürfen.

Das ist bereits heute zu erkennen, denn unabhängig von den Features, die uns die Roslyn-Technologie vom Prinzip her mit beiden Compilern gleichermaßen ermöglicht, sind diese auch sprachmäßig nochmal deutlich erweitert worden. Die neuen Sprachfeatures für Visual Basic möchte dieser Artikel im Folgenden näher erläutern.

Einige Features darunter haben den Weg bereits jetzt mit der vorliegenden Roslyn-Preview in die Sprache gefunden, diese können Sie direkt ausprobieren. Bei anderen Features, die Sie im Folgenden beschrieben finden, handelt es sich hingegen erst um Konzepte, bei denen das Language-Team auch Sie fragt: „Wie würden Sie es machen? Haben Sie vielleicht bessere Ideen?“. Sie haben die Möglichkeit, diese Features mit Ihrer Meinung mitzugestalten. Wie immer sind Abstimmungen auf visualstudio.uservoice.com die beste Möglichkeit, um direkt mit dem Language-Team in Verbindung zu treten und ihm auf diese Art mitzuteilen, welche Funktionen Sie gerne in Visual Basic oder C# sehen würden. Wenn Sie hingegen Fehler im Produkt vermuten, nutzen Sie am besten connect.microsoft.com, um sich mitzuteilen.

Falls Sie die Roslyn-Bits selbst ausprobieren möchten, können Sie das sofort tun. Sie brauchen dafür nicht einmal Visual Studio in einer dedizierten virtuellen Maschine aufzusetzen, da sich der entsprechende VSIX-Installer auf Wunsch wieder rückstandslos aus ihrer Visual-Studio-Produktivumgebung entfernen lässt. Denken Sie aber daran: Roslyn und die Compiler sind zwar Open Source, aber allein, dass sie veröffentlicht wurden, bedeutet nicht, dass sie auch schon fertig sind. Im Gegenteil: Zwar lassen sich auch größere Anwendungen damit kompilieren, aber die Sprachfeatures sind für die nächsten Versionen noch nicht komplett, und auch performancemäßig werden wir noch Verbesserungen sehen. Sie können die aktuelle Public Beta hier herunterladen. Wählen Sie dort den Link zur SDK-Preview. Doch kommen wir nun zu den neuen Sprachfeatures.

„Select Case“ bei „type“

Der VB-Compiler erlaubt es nunmehr, bedingten Code auf Basis des Typs eines Objekts auszuführen, was zu einer vereinfachten Lesbarkeit des Codes führt (Listing 1). Dieses Feature ist insbesondere dann von Vorteil, wenn es notwendig wird, durch eine Auflistung mit heterogenen Elementen zu iterieren, oder an Stellen, an denen aus anderen Gründen auf das Visitor-Entwurfsmuster zurückgegriffen werden muss:

  • Die Ausführung wird beim ersten Case-Block fortgesetzt, dessen deklarierte Variable dem Runtime-Typ des Select-Case-Abfrageausdrucks bzw. einem seiner Basistypen oder den implementierten Schnittstellen entspricht. Da diese Abfragen in der Reihenfolge ihres Vorkommens durchgeführt werden, sollten Sie die Typabfragen zur Vermeidung unbeabsichtigter Kurzschlussauswertungen immer so implementieren, dass die konkreteren Typen vorneweg definiert sind.
  • Nullreferenzen entsprechen nie einem konkreten Typ.
  • Ähnlich wie bei Catch-Blöcken verfügt jede Case-Variable über einen eigenen Codegültigkeitsbereich (Scope), sodass sie sich hinter jedem Zweig erneut verwenden lässt.
  • Zwar können Sie einen geboxten Wert auf den Nullable-Wertetyp hin abfragen, da aber eine erfolgreiche Übereinstimmung niemals in einem wirklichen Nullwert aufgehen wird, ist der Einsatz dieser Möglichkeit von zweifelhaftem Nutzen. Verwenden Sie stattdessen lieber den entsprechenden Nicht-Nullbaren-Typen. Das macht ein zusätzliches Zugreifen auf die Ergebnisvariable effizienter, da Sie keine weiteren Nullüberprüfungen oder Konvertierungen durchführen müssen.
  • Sie können andere Typabfragen im selben Block mit anderen Select-Case-Überprüfungen kombinieren.
  • Wie mit den anderen Variationen von Select Case können Sie mehrere Tests in einem einzelnen Codeblock mit einer durch Komma getrennten Liste angeben. In diesen Fällen weist die als Erstes zugetroffene Abfrage den Wert zu, während den Variablen eines jeden anderen Tests Nullwerte zugewiesen werden.
Function GetArea(shape As Shape) As Double
    Select Case shape
        Case c As Circle
            Return Math.Pi * c.Radius ^ 2
        Case rt As RightTriangle
            Return rt.Base * rt.Height / 2
        Case r As Rectangle
            Return r.Width * r.Height
        Case Else
            Return Double.NaN
    End Select
End Function

Aufmacherbild: Background of old vintage letterpress type letters von Shutterstock / Urheberrecht: aastock

[ header = Seite 2: Test durch „Select Case“ auf Referenzgleichheit ]

Test durch „Select Case“ auf Referenzgleichheit

Nunmehr ist es möglich, Code bedingt durch die Identität eines Objekts ausführen zu lassen (Listing 2).

  • Die Ausführung wird bei dem ersten Case-Block fortgesetzt, dessen Ausdruck derselben Instanz zugeordnet ist wie die des Select-Case-Abfrageausdrucks.
  • Die Verwendung des IsNot-Operators ist gestattet, typischerweise ist das bei einem Default Case IsNot Nothing von Vorteil.
  • Gestattet sind ebenfalls Nullabfragen für Nullable-Value-Typ-Werte. Das ermöglicht ein bequemes Konstrukt, um beispielsweise die drei Zustände eines Nullable Boolean zu überprüfen: Case True, Case False, Case Is Nothing.
  • Sie können diese abermals mit anderen Select-Case-Abfragen im gleichen Codeblock mischen.
Private Sub Button_Click(sender As Object, e As EventArgs) Handles OKButton.Click, CancelButton.Click

    Select Case sender
        Case Is OKButton
            DialogResult = DialogResult.OK
            Close()
        Case Is CancelButton
            DialogResult = DialogResult.Cancel
            Close()
        Case Is Nothing
            Throw New NullReferenceException("sender")
        Case Else
            Debug.Fail("Unknown sender.")
    End Select      
End Sub

„When“-Filter bei „Case“-Blöcken

Zusätzlich zu Abfragen für jede Case-Anweisung (wobei Case Else nicht mitgezählt wird), gibt es die Möglichkeit, dem Abfrageausdruck eine einzelne optionale When-Klausel hinzuzufügen (Listing 3). Diese Variation ist dann nützlich, wenn Sie eine Case-Bedingung weiter verfeinern möchten, nachdem die primäre Abfrage bereits ausgewertet wurde, aber bevor ein folgender Case-Block zur Auswertung ansteht, falls die entsprechende Bedingung falsch war.

Function ComputeSum(obj As Object) As Integer
    Select Case obj
        Case value As Integer
            Return value

        Case arr As Integer() When arr.Length = 0
            Return 0

        Case arr As Integer() When arr.Length = 1
            Return arr(0)

        Case arr As Integer() When arr.Length = 2
            Return arr(0) + arr(1)

        Case arr As Integer()
            Dim result = 0

            For i = 0 To arr.Length - 1
                result += arr(i)
            Next

            Return result

        Case Else
            Throw New NotSupportedException("Kann nicht aufsummiert werden!")
    End Select
End Function

Kommentare nach impliziten Zeilenfortsetzungen

Implizite Zeilenfortsetzungen beschreiben die Fähigkeit von Visual Basic, Zeilenumbrüche auch ohne ein Zeilenfortsetzungszeichen zu implementieren (Listing 4). Dabei erkennt der Editor im Kontext, ob sich die jeweils nächste physische Zeile zu einer logischen Zeile zusammenfassen lässt: Nach einem Komma oder einer beginnenden Klammer muss die logische Codezeile beispielsweise in der nächsten physischen Zeile weiterlaufen, deswegen kann hier, wie in vielen anderen Fällen, auf das Zeilenfortsetzungszeichen („_“) verzichtet werden.

    Protected Overrides Function FindSubElementsForTouchTargeting(point As Point,
                        boundingRect As Rect) As IEnumerable(
                                        Of IEnumerable(Of Point))
        Return MyBase.
            FindSubElementsForTouchTargeting(point,
                    boundingRect)
    End Function

Früher war es nicht möglich, Kommentare hinter solchen umbrochenen logischen Codezeilen zu setzen. Derartige Kommentarzeilen sind mit der Roslyn-VB-Version ab sofort erlaubt (Listing 5).

        Dim einladungen = {"Lucian",   ' den müssen wir einladen! 
                           "Mads",     ' und den natürlich auch.
                           "Adriana"}  ' darf nie fehlen! 🙂

        Dim addrs = From i In einladungen      ' durch die Liste iterieren
                    Let addr = Lookup(i)       ' und nachschlagen.
                    Select i, addr

„ReadOnly“-Autoeigenschaften

In den Versionen seit Visual Basic 2010 sind automatisch implementierte Eigenschaften (Auto Implementing Properties) immer als Read/Write-Operation ausgelegt. Wenn wir eine ReadOnly-Autoeigenschaft als solche definieren, legt der Compiler automatisch ein entsprechendes ReadOnly-BackingField (Klassenvariable) an. Diesem Backing Field, dessen Name sich wie bei den üblichen automatisch implementierten Eigenschaften aus dem Property-Namen mit einem vorangestellten Underscore-Zeichen ableitet, kann dann ein entsprechender Wert zugewiesen werden. Das ist ein wenig unglücklich, denn da das Backing Field IntelliSense nicht bekannt gemacht wird, weiß im Grunde niemand von diesem Feld. Aus diesem Grund könnte sich dieses Feature in Zukunft bei der Detailimplementierung noch ändern. Eine alternative Möglichkeit wäre, an den gleichen Stellen den Schreibzugriff auf ReadOnly Properties zu gestatten, an denen auch der Zugriff auf ReadOnly-Backing-Fields schon erlaubt ist (Listing 6).

Class Kunde

    Sub New(name As String, adresse As String)
        Me.Name = name
        Me.Adress = adresse
    End Sub

    ReadOnly Property Name As String
    ReadOnly Property Adresse As String
    ReadOnly Property ZuletztGeändert As DateTime = DateTime.Now

End Class

Mehrzeilige Strings (Multiline Strings)

In der neuen Roslyn-gepowerten Version erlaubt Visual Basic mehrzeilige Strings. Momentan kann man sich ein wenig mit XML-Literalen in VB behelfen, Multiline-Strings in Visual Basic quasi zu emulieren (Listing 7).

        Dim x = .Value

        Dim y = "Auf diese Weise könnte es"
doch viel besser gehen"

[ header = Seite 3: Zeichenkettenumgestaltung ]

Zeichenkettenumgestaltung (String Interpolation)

Wer in aktuellen Versionen von Visual Basic String.Format mit einer Reihe von Parametern verwendet, kommt schnell in die Bredouille, die Parameter {0}, {1}, {2} usw. zu vertauschen. Darüber hinaus werden solche String.Format-Ausdrücke bei komplexeren Ausdrücken sehr schnell schwer lesbar. Idee: Visual Basic könnte String Interpolation (etwa: Zeichenkettenumgestaltung) erlauben, ein Feature, das man schon aus zahlreichen anderen Sprachen kennt. Listing 8 zeigt Vorschläge für entsprechende Syntaxvariationen – man verwendet das $-Zeichen am Anfang eines zu interpolierenden Strings und interpoliert einen Ausdruck mithilfe geschweifter Klammern, in denen dann Formatanweisungen untergebracht werden. Diese Beispiele zeigen auch einen Vorschlag für die entsprechende Semantik: Der Ausdruck soll in String.Format in InvariantCulture übersetzt werden, dem Compiler aber die Optimierung durch String.Concat überlassen werden oder was auch immer ihm zur Performancesteigerung als noch geeigneter erscheint.

Dim query = $"http://{ url }?name={ Escape(name) }&id={ Escape(id) }&o=xml"

' = String.Format("http://{0}?name={1}&id={2}&o=xml",
' url, Escape(name), Escape(id),
' CultureInfo.InvariantCulture) 

Dim fn = $"{ drive }:{ folder }{ name }.{ ext }"

' = String.Format("{0}:{1}{2}.{3}",
' drive, folder, name, ext,
' CultureInfo.InvariantCulture)


Dim csv = $"Name,FractionIn,FractionOut
{ Code1.Name },{ Code1.FIn :0.00},{ Code1.FOut :0.00}"

' = String.Format("Name,FractionIn,FractionOut{0}{1},{2:0.00},{3:0.00}",
' vbCrLf, Code1.Name, Code1.FIn, Code1.FOut,
' CultureInfo.InvariantCulture) 

Da dieses Feature in seiner Konzeptionierung noch mit einer Menge an Fragezeichen versehen ist, es auf der anderen Seite aber eine Menge Potenzial bietet, die Produktivität enorm zu steigern, sollten Sie für dieses Feature auf visualstudio.uservoice.com voten, falls es Ihnen ausreichend wichtig erscheint. Sowohl für Visual Basic als auch für C# steht zum aktuellen Zeitpunkt noch ein dickes „vielleicht“ vor der Wahrscheinlichkeit, dass dieses Feature wirklich den Weg in die Sprachen findet.

Vereinfachen von LINQ-Ausdrucksbenennungen

Aktuell verhalten sich Select-Klauseln so, dass VB unter der Haube einen anonymen Typen generiert und versucht, entsprechende Namen für die Member zu generieren. Im untenstehenden Beispiel versucht dieser Algorithmus, den Namen ToString zu verwenden – dieser ist jedoch als Member nicht erlaubt, weswegen eine Fehlermeldung generiert wird. Idee: Sollte Select die letzte Klausel sein und nur ein Element verwenden, möge der Compiler die Member-Namensgenerierung dafür komplett auslassen – auch dann, wenn der Klausel noch Distinct, Skip oder Take folgt:

        Dim args As String()

        Dim q = From arg In args
                Select arg.ToString()

        ' BC36606: Range variable name cannot match the
        ' name of a member of the 'Object' class

Datums-Literale im ISO-Format

Früher erkannte Visual Basic Datums-Literale nur im US-Format. Nach dem Update erkennt der Compiler das international gültige (ISO-)Format ebenfalls:

        Dim us_format = #2/3/2013 17:52:51#
        Dim iso_format = #2013-2-3 17:52:51#

Binary Literals (Binärliterale)

Während Sprachen wie F#, Java und einige Versionen von C++ schon länger Binary Literals (Binärliterale) zur Verfügung stellten, bot Visual Basic bisher nur solche für dezimale, oktale und hexadezimale Literale. Fortan erlaubt der Compiler mithilfe des &B-Präfixes auch Binärliterale:

Dim code = &B001010

Zifferngruppentrennzeichen

Musste man bislang ein 32-Bit-Literal ausschreiben, waren aufgrund der schieren Masse an Ziffern Fehler buchstäblich vorprogrammiert. In der neuesten Roslyn-Version von Visual Basic erlaubt der Compiler bereits Zifferngruppentrennzeichen durch Leerzeichen:

Enum E
    Auto = &B 00 01
    Fahrrad = &B 00 10
    ZuFuß = &B 00 11

    Schnellstes = &B 01 00
    Schmalstes = &B 10 00
    landschaftlich_schön = &B 11 00
End Enum

Partial Interfaces und Module

In der aktuellen VB-Version lassen sich nur partielle Klassen erstellen. Für diejenigen, die beispielsweise Codegeneratoren entwickeln, kann das ein Problem darstellen. Entwickler, die generierte Interfaces oder Module erweitern, verlieren nämlich ihren Code, falls dieser durch Veränderung der Vorlage neu generiert werden muss. In der Roslyn-VB-Version lassen sich Interfaces und Module ebenfalls als Partial definieren, wie es für Interfaces in C# bereits möglich ist:

' a.vb
Partial Interface I
    Sub VomEntwicklerVerfasst()
End Interface

' a.g.vb
Partial Interface I
    Sub Codegeneriert()
End Interface

Params „IEnumerable“

In der derzeitigen Version von Visual Basic muss ein ParamArray wirklich vom Typ Array sein. Entwickler verfassen jedoch für gewöhnlich lieber Methoden, die Parameter vom Typ IEnumerable anstelle solcher vom Typ Array erwarten. Idee: Visual Basic könnte das Deklarieren von Methoden erlauben, die ein ParamArray vom Typ IEnuerable(Of T) entgegennehmen:

    Sub f(ParamArray x As IEnumerable(Of String))
        ' Falls jemand dies hier mit f("hello", "world") aufruft,
        ' wandelt der Compiler dies immer noch in F({"hello", "world"}) um.
    End Sub

„TypeOf IsNot“

Im Moment sind Entwickler noch gezwungen, negative TypeOf-Tests auf eher umständliche Weise zu formulieren. IsNot ist in der Roslyn-Version von VB nun ebenfalls mit TypeOf erlaubt, was dann folgendermaßen aussieht:

        If Not TypeOf sender Is Button Then... ' umständlich.
        If TypeOf sender IsNot Button Then... ' schöner! 

Null-Propagating Operator

Dieses Feature ist ein hochbewerteter Vorschlag auf UserVoice, und in der Tat würden VB-Entwickler ein viel einfacheres Leben mit der folgenden Funktion haben. Ihre genaue Implementierung ist indes nicht ganz einfach und dabei gibt es viele Details zu beachten. Die Idee wäre, einen ?.-Operator zu implementieren, mit dem Property-Pfade auch dann ausgewertet werden können, wenn eine der Properties eines Objekts Nothing zurückgibt (Null Propagating):

        Dim y As Integer = x?.y?(3)?.z

        ' ?. sowie ?()-Operatoren verhalten sich wie normale .- und ()-Operatoren,
        ' anstatt jedoch eine NullReferenceException auszulösen, liefern sie lediglich den 
        ' Default-Value des letzten Teils des Ausdrucks z zurück. 

Tipp

Hier finden Sie eine Liste zum aktuellen Stand der Implementierungen beider Sprachen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -