Freigeben über


Visual Basic 2010

Neuerungen in Visual Basic 2010

Jonathan Aneja

Seit seiner Einführung im Jahr 1991 war Visual Basic immer ein phänomenales Produktivitätstools für die Erstellung von Anwendungen. Auch fast 20 Jahre später bietet es noch einfachen Zugriff auf das Microsoft .NET Framework, mit dem Entwickler Anwendungen für Desktops, Telefone, Browser und sogar Clouds schreiben können.

Microsoft veröffentlicht in diesem Monat Visual Studio 2010, das Version 10 von Visual Basic beinhaltet (manchmal auch als VB 2010 oder VB10 bezeichnet). Diese Version ist die bisher leistungsstärkste und enthält viele zeitsparende Features, mit denen Entwickler mit weniger Code mehr erreichen können. Hier finden Sie alles, was Sie wissen müssen, um mit Visual Basic in Visual Studio 2010 sofort loslegen zu können.

Parallele Entwicklung:

In der Vergangenheit wurden Visual Basic und C# durch separate Teams entwickelt, was häufig dazu führte, dass Features in einer Sprache früher als in der anderen erschienen. Beispielsweise hatte C# automatisch implementierte Eigenschaften und Auflistungsinitialisierer, die es in Visual Basic nicht gab, und Visual Basic verfügte über Features wie späte Bindung und optionale Parameter, die in C# nicht vorhanden waren. Doch immer wenn ein Feature in einer der Sprachen angeboten wurde, baten viele Kunden darum, das Feature auch in die andere Sprache aufzunehmen.

Als Reaktion auf dieses Feedback hat Microsoft die Teams für Visual Basic und C# zusammengefasst und arbeitet nun mit paralleler Entwicklung. Die Strategie zielt darauf ab, die Sprachen gemeinsam fortzuführen. Wenn eine wichtige Funktion in einer Sprache eingeführt wird, soll sie auch in der anderen vorhanden sein. Das heißt nicht, dass jedes Feature in beiden Sprachen verfügbar ist und genauso funktioniert; jede Sprache hat ihre eigene Vergangenheit, ihre eigene Beschaffenheit – Merkmale, die beibehalten werden sollen. Parallele Entwicklung heißt nicht, dass jede Aufgabe, die Sie in einer Sprache durchführen können, in der anderen genauso einfach ist.

In .NET Framework 4 haben Visual Basic und C# Riesenschritte auf dieses Ziel zu gemacht und jeweils etliche Funktionen übernommen, die in der anderen Sprache bereits vorhanden waren. Doch parallele Entwicklung betrifft nicht nur die Vergangenheit, sondern ist auch eine Strategie für künftige Innovationen in den Sprachen. In diesem Sinne werden in .NET Framework 4 leistungsfähige neue Features wie Dynamic Language Runtime, Einbetten von Interop-Typen und generische Varianz in beiden Sprachen gleichzeitig eingeführt, sodass Entwickler mit Visual Basic und C# alle Vorteile von .NET Framework nutzen können.

Neue Features in Visual Basic 2010

Die neuen Features in Visual Basic 2010 zielen darauf ab, dass Sie mit weniger Code mehr erreichen. Wir (das Visual Basic-Designteam) haben uns die Stellen angesehen, wo Entwickler häufig langwierigen Standardcode schreiben müssen, und haben nach Wegen gesucht, wie der Compiler diese Arbeiten übernehmen könnte. Das war der Gesamtüberblick. Lassen Sie uns nun näher auf einzelne Features eingehen.

Implizite Zeilenfortsetzung

Visual Basic ist eine zeilenorientierte Sprache, die klare englische Syntax verwendet, um die Lesbarkeit zu verbessern. Das führt jedoch häufig zu Codes, die das Limit von 80 Zeichen pro Zeile überschreiten, sodass die Entwickler viele Bildläufe durchführen müssen. Man kann dem Compiler mithilfe eines Unterstrichs mitteilen, dass er die nächste Zeile als Teil der aktuellen Zeile weiterverarbeiten soll (also mehrere physische Zeilen als eine logische Zeile behandeln soll). Doch die häufige Verwendung von Unterstrichen ist lästig und es gibt schon seit Jahren dringende Anfragen, dass der Compiler dies von allein herausfinden soll.

In Visual Basic 2010 ist der Compiler dazu in der Lage. Er weiß, welche Token (Kommas, Klammern und Operatoren) häufig direkt vor dem Zeilenfortsetzungszeichen auftauchen und setzt es selbstständig ein, sodass der Entwickler es nicht mehr tun muss. Beispielsweise ist es unzulässig, eine Visual Basic-Anweisung mit einem Komma zu beenden. Der Compiler weiß das; wenn er also etwa folgenden Token-Stream sieht: {comma, enter}, geht er davon aus, dass ein Zeilenfortsetzungszeichen vorhanden sein sollte, wie das Beispiel in Abbildung 1 zeigt.

Abbildung 1 Ableitung der Zeilenfortsetzung

<Extension()>
Function FilterByCountry(
  ByVal customers As IEnumerable(Of Customer),
  ByVal country As String) As IEnumerable(Of Customer)
    Dim query =
      From c In customers
      Where c.Country = country
      Select <Customer>
        <%=
          c.Name &
          "," &
          c.Country
        %>
      </Customer>
    Return query
  End Function

In Visual Basic 2008 würde der Code aus Abbildung 1 neun Unterstriche enthalten. In allen diesen Fällen hat der Compiler abgeleitet, wann ein Unterstrich nötig ist, und hat die Auslassung zugelassen:

  • Nach dem Attribut <Extension()>
  • Nach der ( (öffnenden Klammer) in der Methodendeklaration
  • Nach dem , (Komma) für den ersten Parameter
  • Vor der ( (schließenden Klammer) in der Methodendeklaration
  • Nach dem = (Gleichheitszeichen)
  • Nach dem <%= (Anfangstag für einen eingebetteten Ausdruck)
  • Nach jedem & (kaufmännisches Und-Zeichen) im XML-Literal
  • Vor dem %> (Abschlusstag für einen eingebetteten Ausdruck)

Diese neue Compiler-Funktion ist besonders praktisch für die Methodensignatur, die im Beispiel weit über 80 Zeichen aufweisen würde, wenn alle Teile auf derselben Zeile wären. In Abbildung 2 sehen Sie alle Kombinationen aus Token und Platzierungen, in denen das Zeilenfortsetzungszeichen implizit ist.

Abbildung 2 Implizite Fortsetzungszeichen

Token Vor Nach
, (Komma),   .  (Punkt),   >  (Attribute),   (  {  (öffnende Klammern),   <%=  (Beginn eines eingebetteten Ausdrucks (XML-Literale))   X
),   },   ,   ]  (schließende Klammern), %> (Ende eines eingebetteten Ausdrucks) X  

Alle LINQ-Schlüsselwörter:

Aggregate, Distinct, From, Group By, Group Join, Join, Let, Order By, Select, Skip, Skip While, Take, Take While, Where, In, Into, On, Ascending, Descending

X X

Operatoren:

+ ,   - ,   * ,   / ,   \ ,   ^ ,   >> ,   << ,   Mod,   & ,   += ,   -= ,   *= ,   /= ,   \= ,   ^= ,   >>= ,   <<= ,   &= ,   < ,   <= ,   > ,   >= ,   <> ,   Is,  IsNot,  Like,  And,   Or,  Xor,  AndAlso,  OrElse

  X
With (in einem Objektinitialisierer)   X

Wie Sie sehen können, gibt es mehr als 60 Stellen, wo in der Sprache kein Unterstrich erforderlich ist. (Genau genommen ist in keinem der Codebeispiele in diesem Artikel ein Zeilenfortsetzungszeichen erforderlich.) Natürlich können Sie die Unterstriche trotzdem verwenden; Code aus früheren Visual Basic-Versionen wird weiterhin wie erwartet kompiliert.

Anweisungs-Lambdas

Der Begriff Lambda klingt zuerst ungewohnt, aber Lambda ist einfach eine Funktion, die innerhalb einer anderen Funktion definiert ist. In Visual Basic 2008 wurden Lambda-Ausdrücke mit dem Schlüsselwort "Function" eingeführt:

Dim customers As Customer() = ...

 Array.FindAll(customers, Function(c) c.Country = "Canada")

Lambda-Ausdrücke bieten eine kompakte Möglichkeit, Logik lokal auszudrücken, ohne sie auf mehrere Methoden aufteilen zu müssen. Hier sehen Sie beispielsweise, wie der vorhergehende Code in Visual Basic 2005 ausgesehen hätte (wo Lambda-Ausdrücke nicht unterstützt wurden):

Dim query = Array.FindAll(customers, AddressOf Filter)

    ...

Function Filter(ByVal c As customer) As Boolean
  Return c.Country = "Canada"
End Function

Leider erforderten die Lambda-Ausdrücke in Visual Basic 2008, dass die Ausdrücke einen Wert zurückgeben, daher führt dies:

Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))

zu folgendem Ergebnis:

'Compile error: "Expression does not produce a value."

Console.WriteLine ist eine Subprozedur (leer in C#), daher gibt sie keinen Wert zurück und der Compiler meldet einen Fehler. Um dies zu vermeiden wurde in Visual Basic 2010 Unterstützung für Anweisungs-Lambdas eingeführt. Dabei handelt es sich um Lambdas, die eine oder mehrere Anweisungen enthalten können:

Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))

Da Console.WriteLine keinen Wert zurückgibt, können wir eine Sub-Lambda statt einer Function-Lambda erstellen. Hier ist ein weiteres Beispiel mit mehreren Anweisungen:

Array.ForEach(customers, Sub(c)
                           Console.WriteLine("Country Name:")
                           Console.WriteLine(c.Country)
                         End Sub)

Beim Ausführen gibt dieser Code zwei Zeilen für jeden Kunden aus. Wenn Sie bei der Codierung die Maustaste über chalten, sehen Sie, dass der Compiler den Typ als „Customer“ abgeleitet hat (Sie können auch c As Customer eingeben, um den Typ explizit anzugeben). Die dynamische Verknüpfung von Ereignishandlern ist eine weitere gute Möglichkeit zur Verwendung von Anweisungs-Lambdas:

AddHandler b.Click, Sub(sender As Object, e As EventArgs)
                      MsgBox("Button Clicked")
                      'insert more complex logic here
                    End Sub

Genau genommen können Sie Anweisungs-Lambdas mit einem Feature kombinieren, das in Visual Basic 2008 eingeführt wurde: lockere Delegaten. (Sie können Delegaten – typensichere Zeiger auf Funktionen – zur Ausführung von mehreren Funktionen gleichzeitig verwenden.) Diese Kombination ergibt sogar eine noch einfachere Signatur:

AddHandler b.Click, Sub()
                      MsgBox("Button Clicked")
                     'insert more complex logic here
                    End Sub

Die Delegatlockerung ermöglicht das komplette Auslassen von Parametern aus einem Ereignishandler, was von Vorteil ist, da diese häufig nicht benötigt werden und somit nur als visuelle Störung dienen.

Zusätzlich zu den einzeiligen Sub-Lambdas und mehrzeiligen Sub-Lambdas, die wir eben gesehen haben, unterstützt Visual Basic 2010 auch mehrzeilige Function-Lambdas:

Dim query = customers.Where(Function(c)
                              'Return only customers that have not been saved
                              'insert more complex logic here
                              Return c.ID = -1
                            End Function)

Ein weiterer interessanter Aspekt von Anweisungs-Lambdas ist die Überschneidung mit den in Visual Basic 2008 eingeführten anonymen Delegaten. Diese werden häufig mit den anonymen Methoden aus C# verwechselt, obwohl sie technisch gesehen unterschiedlich sind. Anonyme Delegaten treten auf, wenn der Visual Basic-Compiler einen Delegattypen basierend auf der Lambda-Methodensignatur ableitet:

Dim method = Function(product As String)
               If product = "Paper" Then
                 Return 4.5 'units in stock
               Else
                 Return 10 '10 of everything else
               End If
             End Function

MsgBox(method("Paper"))

Wenn Sie diesen Code ausführen, wird der Wert 4.5 im Meldungsfeld angezeigt. Wenn Sie den Mauszeiger über method halten, wird außerdem der Text Dim method As <Function(String) As Double> angezeigt. Da wir keinen eigentlichen Delegattyp zur Verfügung gestellt haben, erstellt der Compiler ihn automatisch:

Delegate Function $compilerGeneratedName$(product As String) As Double

Dies wird als anonymes Delegat bezeichnet, da es nur in dem vom Compiler produzierten Code angezeigt wird, nicht im geschriebenen Code. Beachten Sie, dass der Compiler den Rückgabetyp als Double abgeleitet hat, obwohl keine As-Klausel für den Lambda-Rückgabetyp angegeben war. Der Compiler betrachtet alle Rückgabeanweisungen im Lambda und sieht die Typen Double (4.5) und Integer (10):

'Notice the "As Single"
Dim method = Function(product As String) As Single
               If product = "Paper" Then
                 Return 4.5 'units in stock
               Else
                 Return 10 '10 of everything else
               End If
             End Function

Anschließend führt er den dominanten Typalgorithmus aus und bestimmt, dass er 10 problemlos in Double konvertieren kann, aber nicht 4.5 in Integer; daher ist Double die bessere Wahl.

Sie können den Rückgabetyp auch explizit steuern, woraufhin der Compiler nicht versucht, den Typ abzuleiten. Statt sich darauf zu verlassen, dass der Compiler den Delegattyp ableitet, wird ein Lambda häufig einer Variable zugewiesen, die einen expliziten Delegattyp hat:

Dim method As Func(Of String, Single) =
  Function(product)
    If product = "Paper" Then
      Return 4.5 'units in stock
    Else
      Return 10 '10 of everything else
    End If
  End Function

Da ein expliziter Zieltyp zur Verfügung gestellt wurde, ist es nicht notwenig, As String oder As Single anzugeben; der Compiler kann ihr Vorhandensein basierend auf dem Delegattyp auf der linken Seite der Anweisung ableiten. Wenn Sie den Mauszeiger über product halten, werden Sie feststellen, dass der abgeleitete Typ String ist. Es ist nicht mehr nötig, As Single anzugeben, da der Delegattyp diese Informationen bereits zur Verfügung stellt. Im vorhergehenden Beispiel sieht die Signatur des Func-Delegats (welches .NET Framework umfasst) wie folgt aus:

Delegate Function Func(Of T, R)(ByVal param As T) As R

mit einer kleinen Ausnahme, wie wir später im Abschnitt zur generischen Varianz sehen werden.

Automatisch implementierte Eigenschaften

In Visual Basic sind Eigenschaften Klassenmitglieder, die dazu dienen, den Objektstatus nach außen mitzuteilen. Eine typische Eigenschaftsdeklaration sieht etwa wie folgt aus:

Private _Country As String

Property Country As String
  Get
    Return _Country
  End Get
  Set(ByVal value As String)
    _Country = value
  End Set
End Property

Das sind 10 Zeilen Code für ein eigentlich sehr einfaches Konzept. Wenn man davon ausgeht, dass typische Objekte häufig Dutzende von Eigenschaften haben, haben Sie letztendlich eine Menge Standardcode in Klassendefinitionen. Um solche Aufgaben zu vereinfachen, wurden in Visual Basic 2010 automatisch implementierte Eigenschaften eingeführt, anhand derer Sie eine einfache Eigenschaft mit nur einer Codezeile definieren können:

Property Country As String

In diesem Fall fährt der Compiler fort und erzeugt Getter, Setter und die Sicherungsfelder automatisch. Der Name des Sicherungsfelds beginnt immer mit einem Unterstrich, gefolgt vom Namen der Eigenschaft: _Country in diesem Fall. Diese Namenskonvention sorgt für die binäre Serialisierungskompatibilität, falls eine automatisch implementierte Eigenschaft in eine reguläre geändert wird. Solange der Name des Sicherungsfelds gleich bleibt, funktioniert die binäre Serialisierung.

Eines der neuen Dinge, die Sie mit automatisch implementierten Eigenschaften ausführen können, ist die Angabe von Initialisierern, die beim Ausführen des Konstruktors den Standardwert der Eigenschaft festlegen können. Ein häufiges Szenario mit Entitätsklassen setzt beispielsweise den Primärschlüssel auf -1, um anzugeben, dass er noch nicht gespeichert wurde. Der Code würde in etwa wie folgt aussehen:

Property ID As Integer = -1

Beim Ausführen des Konstruktors wird das Sicherungsfeld (_ID) automatisch auf den Wert -1 gesetzt. Die Syntax des Initialisierers funktioniert auch für Referenztypen:

Property OrderList As List(Of Order) = New List(Of Order)

Die letzte Codezeile mag vielleicht nicht sehr nach Visual Basic klingen, da die doppelte Eingabe des Typs redundant ist. Doch es gibt sogar eine kürzere Syntax, die berücksichtigt, was Visual Basic in regulären Variablendeklarationen zulässt:

Property OrderList As New List(Of Order)

Sie können dies sogar mit Objektinitialisierern kombinieren, um das Festlegen zusätzlicher Eigenschaften zu ermöglichen:

Property OrderList As New List(Of Order) With {.Capacity = 100}

Natürlich ist für komplexere Eigenschaften weiterhin die erweiterte Syntax nötig. Sie können immer noch Property{Tab} eingeben, um den bisherigen Eigenschaftenausschnitt zu aktivieren. Alternativ können Sie nach der ersten Zeile der Eigenschaft einfach Get{Enter} eingeben, damit die IDE die herkömmliche Eigenschaft erzeugt:

Property Name As String
  Get

  End Get
  Set(ByVal value As String)

  End Set
End Property

Einige Benutzer haben angemerkt, dass die neue Eigenschaftensyntax fast identisch mit der Syntax für ein öffentliches Feld ist, also warum sollte man kein öffentliches Feld verwenden? Dafür gibt es einige Gründe:

  • Ein Großteil der .NET-Datenbindungsinfrastruktur arbeitet mit Eigenschaften, aber nicht mit Feldern.
  • Eine Schnittstelle kann das Vorhandensein eines Felds nicht erzwingen, jedoch das einer Eigenschaft.
  • Eigenschaften bieten längerfristige Flexibilität bei Änderungen der Geschäftsregeln. Angenommen, jemand führt die Regel ein, dass eine Telefonnummer 10 Ziffern enthalten muss. Es gibt keine Möglichkeit, diese Validierung bei der Zuordnung zu einem öffentlichen Feld durchzuführen. Das Ändern eines öffentlichen Felds in eine Eigenschaft ist eine bedeutende Änderung für Szenarios wie binäre Serialisierung und Reflektion.

Auflistungsinitialisierer

Eine häufige .NET-Vorgehensweise ist die Instanziierung einer Auflistung, die dann durch Aufrufen der Add-Methode für jedes Element einzeln ausgefüllt wird:

Dim digits As New List(Of Integer)
digits.Add(0)
digits.Add(1)
digits.Add(2)
digits.Add(3)
digits.Add(4)
digits.Add(5)
digits.Add(6)
digits.Add(7)
digits.Add(8)
digits.Add(9)

Doch das Ergebnis ist ein hoher Syntaxaufwand für ein eigentlich sehr einfaches Konzept. In Visual Basic 2010 wurden Auflistungsinitialisierer eingeführt, mit denen Sie Auflistungen einfacher instanziieren können. Mit folgendem Code:

Dim digits = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}

erzeugt der Compiler alle Aufrufe für die Add-Methode automatisch. Sie können das Feature auch mit der Visual Basic-Syntax „As New“ verwenden:

Dim digits As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}

Im Visual Basic-Team empfehlen wir immer die zweite Syntax (As New) gegenüber der vorhergehenden, da sie den Code vor Änderungen an der Einstellung „Option Infer“ schützt.

Sie können die Auflistungsinitialisierer für alle Typen verwenden, die folgende Voraussetzungen erfüllen:

  • Sie können ihn mit einer „For Each“-Anweisung durchlaufen – das heißt, er implementiert „IEnumerable“. (Eine präzisere Definition eines Auflistungstyps finden Sie in Abschnitt 10.9.3 der Sprachspezifikation für Visual Basic unter msdn.microsoft.com/library/aa711986(VS.71).aspx.)
  • Er hat einen zugänglichen (nicht unbedingt öffentlichen) parameterlosen Konstruktor.
  • Er hat eine zugängliche (nicht unbedingt öffentliche) Instanz- oder Erweiterungsmethode namens „Add“.

Das heißt, Sie können Auflistungsinitialisierer auch mit komplexeren Typen verwenden, z. B. Wörterbüchern:

Dim lookupTable As New Dictionary(Of Integer, String) From
  {{1, "One"},
   {2, "Two"},
   {3, "Three"},
   {4, "Four"}}

(Beachten Sie, dass hier keine Unterstriche vorhanden sind, obwohl die Anweisungen über fünf Zeilen geht.) In diesem Fall erzeugt der Compiler Code, der der alten Methode zur Initialisierung des Wörterbuchs entspricht:

Dim lookupTable As New Dictionary(Of Integer, String)
lookupTable.Add(1, "One")
lookupTable.Add(2, "Two")
lookupTable.Add(3, "Three")
lookupTable.Add(4, "Four")

Der Compiler ruft eine Add-Methode auf, die zwei Parameter statt nur einem hat. Er weiß, dass er das tun muss, da die an den Auflistungsinitialisierer übergebenen Werte in verschachtelten Klammern waren, wie folgt: {{1, "One"}, {2, "Two"}, …}. Der Compiler versucht, für jede verschachtelte Klammer die Parameter an eine kompatible Add-Methode zu übergeben.

Sie können auch eine eigene benutzerdefinierte Add-Implementierung mittels einer Erweiterungsmethode bereitstellen:

<Extension()>
  Sub Add(ByVal source As IList(Of Customer),
          ByVal id As Integer,
          ByVal name As String,
          ByVal city As String)

      source.Add(New Customer With
                 {
                    .ID = id,
                    .Name = name,
                    .City = city
                 })
  End Sub

(Beachten Sie die fehlenden Unterstriche!) Diese Methode erweitert jeden Typ, der IList(Of Customer) implementiert, und ermöglicht Ihnen die Verwendung der neuen Auflistungsinitialisierer-Syntax, wie folgt:

Dim list = New List(Of Customer) From
            {
              {1, "Jon", "Redmond"},
              {2, "Bob", "Seattle"},
              {3, "Sally", "Toronto"}
            }

(Fügt drei Kunden zu list hinzu). Sie können die Auflistungsinitialisierer auch zusammen mit automatisch implementierten Eigenschaften verwenden:

Property States As New List(Of String) From {"AL", "AK", "AR", "AZ", ...}

Array-Literale

Zusätzlich zu den leistungsfähigeren Möglichkeiten bei der Arbeit mit Auflistungstypen bietet Visual Basic 2010 auch einige großartige Verbesserungen für die Arbeit mit Arrays. Sehen Sie sich folgenden Code an (der in älteren Versionen hervorragend funktioniert):

Dim numbers As Integer() = New Integer() {1, 2, 3, 4, 5}

Wenn man sich die Elemente im Array anschaut, sieht man, dass es sich jeweils um einen Integer-Wert handelt. Es bringt also keinen zusätzlichen Nutzen, in dieser Zeile zweimal „Integer“ auszuschreiben. Array-Literale ermöglichen das Erstellen eines Arrays, indem alle seine Elemente in Klammern gesetzt werden und der Compiler anschließend den Typ automatisch ableitet:

Dim numbers = {1, 2, 3, 4, 5}

Der Typ von numbers ist nicht Object, sondern Integer() (solange Option Infer aktiviert ist), da das Array-Literal jetzt eigenständig stehen kann und einen eigenen Typ hat. Betrachten wir ein etwas komplizierteres Beispiel:

Dim numbers = {1, 2, 3, 4, 5.555}

In diesem Fall wird der Typ von numbers als Double() abgeleitet. Der Compiler bestimmt den Typ, indem er jedes Element im Array überprüft und den dominanten Typ berechnet (mit demselben Algorithmus, den wir zuvor zur Ableitung des Rückgabetyps für Anweisungs-Lambdas erläutert haben). Was passiert, wenn kein dominanter Typ vorhanden ist, wie im folgenden Code:

Dim numbers = {1, 2, 3, 4, "5"}

In diesem Fall wäre das Konvertieren von Integer in String eine einschränkende Konvertierung (das heißt, es entsteht ein potenzieller Datenverlust zur Laufzeit); umgekehrt wäre auch die Konvertierung von String in Integer eine einschränkende Konvertierung. Der einzige sichere Typ, der zur Auswahl steht, ist Object() (und der Compiler gibt einen Fehler aus, wenn Option Strict aktiviert ist).

Array-Literale können verschachtelt werden, um entweder mehrdimensionale Arrays oder Zacken-Arrays zu bilden:

'2-dimensional array
Dim matrix = {{1, 0}, {0, 1}} 

'jagged array - the parentheses force evaluation of the inner array first
Dim jagged = { ({1, 0}), ({0, 1}) }

Dynamic Language Runtime

Obwohl Visual Basic technisch gesehen eine statische Sprache ist, hatte sie immer äußerst leistungsfähige dynamische Funktionen, wie z. B. späte Bindung. Visual Studio 2010 wird mit einer neuen Plattform, der so genannten Dynamic Language Runtime (DLR) geliefert, die das Erstellen von und die Kommunikation zwischen dynamischen Sprachen erleichtert. Visual Basic 2010 wurde aktualisiert, um DLR in der späten Bindung vollständig zu unterstützen, sodass Entwickler Bibliotheken und Frameworks aus anderen Sprachen wie IronPython/IronRuby verwenden können.

Das Tolle an diesem Feature ist, dass an der Syntax nichts geändert wurde (genau genommen wurde nicht eine einzige Codezeile im Compiler geändert, um dieses Feature zu unterstützen). Entwickler können weiterhin späte Bindung für Vorgänge festlegen, wie sie es in den früheren Visual Basic-Versionen auch getan haben. Geändert wurde der Code in Visual Basic Runtime (Microsoft.VisualBasic.dll), der jetzt die von DLR bereitgestellte IDynamicMetaObjectProvider-Schnittstelle erkennt. Wenn ein Objekt diese Schnittstelle implementiert, konstruiert Visual Basic Runtime eine DLR-CallSite und ermöglicht, dass das Objekt und die dazugehörige Sprache ihre eigene Semantik in den Vorgang einbringen.

Beispielsweise enthalten die Python Standard Libraries eine Datei namens random.py mit einer Shuffle-Methode, die verwendet werden kann, um Elemente in einem Array in zufälliger Reihenfolge neu anzuordnen. Der Aufruf ist einfach:

Dim python As ScriptRuntime = Python.CreateRuntime()
Dim random As Object = python.UseFile("random.py")

Dim items = {1, 2, 3, 4, 5, 6, 7}
random.shuffle(items)

Bei der Ausführung sieht Visual Basic, dass das Objekt IDynamicMetaObjectProvider implementiert, und übergibt daher die Steuerung an die DLR, die wiederum mit Python kommuniziert und die Methode ausführt (und dabei das Array übergibt, das in Visual Basic als Argument für die Methode definiert wurde).

Dies ist ein Beispiel für den Aufruf einer DLR-fähigen API, doch es ist auch möglich, dass Entwickler ihre eigenen APIs erstellen, die dieses Feature verwenden. Das Wichtigste ist die Implementierung der IDynamicMetaObjectProvider-Schnittstelle, damit Visual Basic und C#-Compiler erkennen, dass das Objekt eine spezielle dynamische Semantik hat. Statt die Schnittstelle manuell zu implementieren, ist es einfacher, sie aus der Klasse System.Dynamic.DynamicObject abzuleiten (die diese Schnittstelle bereits implementiert hat) und einfach einige Methoden außer Kraft zu setzen. In Abbildung 3 sehen Sie ein vollständiges Beispiel für das Erstellen eines benutzerdefinierten dynamischen Objekts (ein Eigenschaftsbehälter, der Eigenschaften scheinbar dynamisch erstellt) und den Aufruf mit einer normalen späten Bindung aus Visual Basic. (Weitere Informationen zum Arbeiten mit DynamicObject finden Sie in dem hervorragenden Artikel von Doug Rothaus unter blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx.)

Abbildung 3 Erstellen eines benutzerdefinierten dynamischen Objekts und Aufruf mit einer späten Bindung aus Visual Basic

Imports System.Dynamic
  Module Module1
    Sub Main()
      Dim p As Object = New PropertyBag
        p.One = 1
        p.Two = 2
        p.Three = 3
      Console.WriteLine(p.One)
      Console.WriteLine(p.Two)
      Console.WriteLine(p.Three)
    End Sub
      Class PropertyBag : Inherits DynamicObject
        Private values As New Dictionary(Of String, Integer)
        Public Overrides Function TrySetMember(
          ByVal binder As SetMemberBinder,
          ByVal value As Object) As Boolean
            values(binder.Name) = value
          Return True
        End Function
        Public Overrides Function TryGetMember(
          ByVal binder As GetMemberBinder,
          ByRef result As Object) As Boolean
          Return values.TryGetValue(binder.Name, result)
        End Function
      End Class
  End Module

Generische Varianz

Dies ist ein Feature, das zuerst ziemlich kompliziert klingt (mit Begriffen wie Kovarianz und Kontravarianz), das aber eigentlich ganz einfach ist. Wenn Sie ein Objekt vom Typ IEnumerable(Of Apfel) haben und es einer IEnumerable(Of Frucht) zuordnen möchten, ist das zulässig, da jeder Apfel eine Frucht ist (erzwungen durch eine Vererbungsbeziehung). Leider wurde die generische Varianz vor Visual Basic 2010 im Compiler nicht unterstützt, obwohl sie in der Common Language Runtime (CLR) unterstützt wurde.

Sehen Sie sich das Beispiel in Abbildung 4 an. In Visual Basic 2008 würde der Code aus Abbildung  4 in der Zeile Dim enabledOnly einen Kompilationsfehler verursachen (oder einen Laufzeitfehler, falls Option Strict deaktiviert ist). Dies konnte man mit dem Aufruf der Erweiterungsmethode .Cast umgehen, wie hier gezeigt:

'Old way, the call to Cast(Of Control) is no longer necessary in VB 2010
    Dim enabledOnly = FilterEnabledOnly(buttons.Cast(Of Control))

Dies ist nicht mehr nötig, da in Visual Basic 2010 die IEnumerable-Schnittstelle mit dem Out-Modifizierer als kovariant gekennzeichnet wurde:

Interface IEnumerable(Of Out T)
  ...
End Interface

Abbildung 4 Beispiel für eine generische Varianz

Option Strict On
Public Class Form1
  Sub Form1_Load() Handles MyBase.Load
    Dim buttons As New List(Of Button) From
      {
        New Button With
        {
          .Name = "btnOk",
          .Enabled = True
        },
        New Button With
        {
          .Name = "btnCancel",
          .Enabled = False
        }
      }

    Dim enabledOnly = FilterEnabledOnly(buttons)
  End Sub
  Function FilterEnabledOnly(
    ByVal controls As IEnumerable(Of Control)
    ) As IEnumerable(Of Control)
    Return From c In controls
    Where c.Enabled = True
  End Function
End Class

Das bedeutet, dass der generische Parameter T jetzt variant ist (das heißt, er funktioniert für Vererbungsbeziehungen), und der Compiler stellt sicher, dass er nur an Positionen verwendet wird, wo der Typ aus der Schnittstelle kommt. Generische Parameter können auch kontravariant sein, was bedeutet, dass sie nur an input-Positionen verwendet werden. Ein Typ kann beides enthalten. Das weiter oben erläuterte Func-Delegat hat beispielsweise sowohl kontravariante Parameter (die hineingegeben werden) als auch kovariante Parameter (für den Rückgabetyp):

Delegate Function Func(Of In T, Out R)(ByVal param As T) As R

Sie können die In- und Out-Modifizierer an den benutzerdefinierten Schnittstellen und Delegaten verwenden. Viele häufig verwendete Schnittstellen und Delegate in .NET Framework 4 wurden bereits als variant gekennzeichnet, beispielsweise alle Action/Func-Delegate, IEnumerable(Of T), IComparer(Of T), IQueryable(Of T) und andere.

Das Gute an generischer Varianz ist, dass Sie sich keine Gedanken über dieses Feature machen müssen – wenn es richtig funktioniert, bemerken Sie es gar nicht. Situationen, die zu Kompilationsfehlern führten oder einen Aufruf von .Cast(Of T) erforderten, sollten in Visual Basic 2010 problemlos funktionieren.

Verbesserte optionale Parameter

Optionale Parameter bieten ein praktisches Produktivitätsfeature, mit dem Entwickler flexiblere Methoden entwickeln können, ohne dabei eine Klasse mit zahlreichen Überladungen einer Methode zu überfrachten. Eine Einschränkung war bisher, dass optionale Parameter keine Null-Werte zuließen (oder sonstige unspezifische Strukturtypen). In Visual Basic 2010 können Sie nun optionale Parameter mit jedem Werttyp definieren:

Sub DisplayOrder(ByVal customer As Customer,
                 ByVal orderID As Integer,
                 Optional ByVal units As Integer? = 0,
                 Optional ByVal backgroundColor As Color = Nothing)
End Sub

In diesem Fall hat units den Typ Nullable(Of Integer) und backgroundColor einen unspezifischen Strukturtyp, trotzdem können beide als optionale Parameter verwendet werden. Visual Basic 2010 bietet außerdem bessere Unterstützung für generische optionale Parameter.

Einbetten von Interop-Typen

Für Anwendungen, die COM Interop ausführen, ist ein häufiger Problempunkt die Arbeit mit primären Interopassemblys (PIAs). PIA ist eine .NET-Assembly, die als Runtime Callable Wrapper (RCW) über eine COM-Komponente dient und durch eine eindeutige GUID identifiziert wird. .NET-Assemblys kommunizieren mit der PIA, die dann alle Notwendige veranlasst, um Daten zwischen COM und .NET zu verschieben.

Leider können PIAs die Bereitstellung verkomplizieren, da sie zusätzliche DLLs darstellen, die auf den Computern der Endbenutzer bereitgestellt werden müssen. Dies kann auch Probleme bei der Versionierung verursachen – wenn Sie beispielsweise möchten, dass eine Anwendung mit Excel 2003 und mit Excel 2007 funktioniert, müssen Sie beide PIAs mit der Anwendung bereitstellen.

Das Feature zum Einbetten von Interop-Typen ermöglicht die Einbettung direkt in die Anwendung, doch nur der Typen und Mitglieder aus der PIA, die unbedingt erforderlich sind. Daher müssen keine PIAs auf den Computern der Endbenutzer bereitgestellt werden.

Um dieses Feature für ein vorhandenes Projekt zu aktivieren (für neue Referenzen ist sie standardmäßig aktiviert), wählen Sie die Referenz im Projektmappen-Explorer aus und ändern die Option Embed Interop Types im Eigenschaftenfenster (siehe Abbildung 5). Wenn Sie mithilfe des Befehlszeilencompilers kompilieren, verwenden Sie den Switch /l (oder /link) statt /r und /reference.

Abbildung: Aktivieren des Features zum Einbetten von Interop-Typen im Projektmappen-Explorer

Abbildung 5 Aktivieren des Features zum Einbetten von Interop-Typen im Projektmappen-Explorer

Nachdem Sie das Feature aktiviert haben, ist die Anwendung nicht mehr von der PIA abhängig. Wenn Sie die Assembly in Reflector oder ildasm öffnen, werden Sie sogar feststellen, dass es keinen Verweis auf die PIA gibt.

Festlegung von Zielversionen

Das Beste an all den Features in Visual Basic 2010 ist, dass Sie sie sogar in Projekten verwenden können, die über .NET Framework 3.5 auf .NET Framework 2.0 abzielen. Das bedeutet, dass implizite Zeilenfortsetzung, Array-Literale, Auflistungsinitialisierer, Anweisungs-Lambdas, automatisch implementierte Eigenschaften usw. in vorhandenen Projekten funktionieren, ohne dass extra .NET Framework 4 als Ziel angegeben werden muss.

Die einzige Ausnahme ist das Einbetten von Interop-Typen, da dies von Typen abhängig ist, die nur in .NET Framework 4 vorhanden sind; deshalb können Sie es nicht verwenden, wenn Sie auf .NET Framework 2.0-Versionen über 3.5 abzielen. Darüber hinaus sind Typen, die als variant gekennzeichnet sind, nur in .NET Framework 4 so gekennzeichnet, deshalb müssen Sie in dem oben genannten Beispiel trotzdem .Cast(Of T) aufrufen, wenn Sie auf 2.0-Versionen über 3.5 abzielen. Sie können jedoch eigene Varianztypen erstellen (mit In-/Out-Modifizierern), wenn Sie diese früheren Versionen als Ziel haben.

Um das aktuelle Ziel-Framework für eine Anwendung zu ändern, doppelklicken Sie auf My Project, klicken Sie auf die Registerkarte Compile, dann auf Advanced Compile Options, und treffen Sie im Kombinationsfeld unten auf der Seite eine Auswahl.

Beim Kompilieren von der Befehlszeile aus gibt es keinen Befehlszeilen-Switch zur Aktivierung dieses Features. Stattdessen überprüft der Compiler, welche Assembly die Definition von System.Object zur Verfügung gestellt hat (normalerweise mscorlib) und welches Framework das Ziel dieser Assembly ist, und fügt dann den Wert in die Ausgabeassembly ein. (Dies ist derselbe Mechanismus, den der Compiler beim Erstellen von Silverlight-Assemblys verwendet.) Bei der Verwendung der IDE passiert dies alles transparent, also müssen Sie sich normalerweise keine Gedanken darüber machen.

Probieren Sie es aus

Wie Sie sehen können, bietet Visual Basic 2010 viele leistungsfähige Features, mit denen Sie produktiver arbeiten können, weniger Codezeilen schreiben müssen und mehr Arbeit dem Computer überlassen können. In diesem Artikel habe ich nur die Sprachfeatures erläutert, aber Visual Basic 2010 IDE weist ebenfalls eine Menge Verbesserungen auf. Dies ist ein Teil davon:

  • Navigieren zu
  • Gekennzeichnete Referenzen
  • Generate From Usage
  • Verbessertes IntelliSense (Abgleich von Teilzeichenfolgen, Suche nach Höckerschreibweise, Vorschlagmodus – nützlich für „Zuerst testen“-Entwicklung)
  • Unterstützung von mehreren Bildschirmen
  • Zoom

Das Visual Basic-Team freut sich auf Ihr Feedback und Verbesserungsvorschläge zu Visual Basic, senden Sie uns also Ihre Fragen und Kommentare über Microsoft Connect. Weitere Informationen zur Sprache und zu den IDE-Features finden Sie unter msdn.com/vbasic, einschließlich Artikel, Beispiele und Anleitungsvideos. Natürlich ist die beste Lernmöglichkeit, einfach loszulegen und das Produkt zu verwenden, deshalb sollten Sie es installieren und ausprobieren.

Sie möchten noch mehr Visual Basic? Bitteschön! Das MSDN Magazin *nimmt seine monatliche Basic Instincts-Artikelreihe wieder auf, die sich mit zugehörigen Themen an Visual Basic-Entwickler richtet und vom Visual Basic-Team bei Microsoft geschrieben wird.        *

Jonathan Aneja ist Programmmanager im Entity Framework-Team von Microsoft. Davor war er Programmmanager für den Visual Basic-Compiler für die Visual Basic-Versionen 2008 und 2010. Er ist seit vier Jahren bei Microsoft.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Dustin Campbell, Jason Malinowski und Lucian Wischik