Skip Navigation Links.
Skip Navigation Links.

.net student

microsoft

Vyhledávání v obsahu portálu


Přihlášení uživatele

Přihlášení uživatele

Členové:
  • Poslední nový uživatel Poslední: liborsky
  • Nový dnes Dnes nových 1
  • Nový včera Včera nových: 0
  • Počet uživatelů: Celkem: 4708
Lidé online:
  • Návštěvníci: Návštěvníků: 31
  • Registrovaných Členů: 0
  • Celkem Celkem: 31
Mapa Stranek

Co je nového ve Visual Basicu 9

Kdo byl na letošním MS Festu, mohl se podrobně seznámit s novými vlastnostmi jazyka C#. Kdo tam nebyl, udělal chybu!. Přednášky o C# mne inspirovali k prostudování specifikace jazyka Visual Basic 9 Beta 2 a vyzkoušení novinek. Přináším, doufám že kompletní, přehled novinek ve VB9 (2008) oproti VB8 (2005).

Co je nového ve Visual Basicu 9

Visual Basic 9 je nová verze jazyka Visual Basic, která přichází spolu s .NET frameworkem 3.5 a Visual Studiem 2008. Obsahuje některá vylepšení, rozšíření a opravy chyb. Na ty se v následujícím článku podíváme podrobněji. Tento článek je postaven na verzi Beta 2, takže je možné, že ve finále se ještě něco málo změní, ale myslím si, že moc toho nebude. Nabízí se zde samozřejmě srovnání s C#. V C# ale programuji jen výjimečně, takže nemohu s jistotou říci, co v něm bylo už ve verzi 2.0 a co je nové ve verzi 3.0. Pravdou je, že VB tým je za C# vždy o něco pozadu, a ve VB tak zůstává několik věcí, které nejsou dotažené k úplné dokonalosti. Nadruhou stranu s novou verzí se VB teamu podařilo udělat velký kus práce a některé věci, které ani C# nemá.

Přehled

Podle [1] došlo k následujícím změnám ve specifikaci jazyka:

  1. Důležité změny
  2. Méně důležité změny

Dále jsou zde změny o kterých se specifikace [1] nevím proč nezmiňuje

Já sám bych ještě rád upozornil na další změny, které se přímo netýkají jazyka, ale především jeho integrace do Visual Studia.

  • Intellisense pro klíčová slova
  • Opravené generování XML dokumentace
  • Multitargeting

1. Extension methods (rozšiřující metody)

Představte si třídu, k jejímuž zdrojovému kódu nemáte přístup a potřebujete ji rozšířit o nějakou funkčnost. Například takový String(který je navíc NotInheritable) a určitě u něj strašně postrádáte metodu ToCyrilic(), která by všechny znaky latinky převedla na jejich ekvivalenty v azbuce. Pokud byste takovou metodu potřebovali ve VB8, nezbývá vám než založit nějaký modul, třeba StringToolsa do něj metodu implementovat a pak ji volat nějak jako cyr$ = StringTools.ToCyrilic(lat$). Vám by se přitom líbil intuitivnější způsob - cyr$ = lat$.ToCyrilic().

A to je přesně to co extension methods - rozšiřující metody dělají.

Vezměte svoji metodu ToCyrilica označte ji atributem System.Runtime.CompilerServices.ExtensionAttribute. To vám poté umožní syntaktickou zkratku - volat metodu jako by byla členem třídy String. A to všude tam, kde bude modul StringTools"in scope" (v rozsahu platnosti). To znamená, že pokud tento modul budete mít v jiném namespacu než, kde chcete metodu použít (například v samostatném DLL), stačí když importujete namespace, ve kterém je modu obsažen. Můžete ale importovat i modul samotný.

Rozšiřující metody lze ve VB deklarovat jen na úrovni modulu. V C# zase na úrovni statických tříd jako statické metody těchto tříd. Navíc se v C# neoznačují atributem ExtensionAttribute, ale místo toho se první parametr označí klíčovým slovem this. Výsledek kompilace metody je ale stejný jak z VB tak z C# a Visual Basic umí použít i extension metody deklarované v něčem jiném než ve standardním modulu. Zajímavé je, že kompilátory obou jazyků označí třídu (modul), ve které(m) se rozšiřující metody nacházejí také atributem ExtensionAttribute.

Ukázka

Public Module StringTools
    Public Function ToCyrilic(str As String) As String
        ' ...
    End Function
End Module

Public Module KdeToChciPoužít
    Public Sub Main()
        Dim a As String = "ABC"
        Console.WriteLine(a.ToCyrilic) 'Vypíše АБЦ (kdyby to nějdo implementoval)
    End Sub
End Module

Rozšiřující lze deklarovat pouze metody (Sub, Function) nikoliv třeba vlastnosti.

2. Partial methods (roztrhané metody)

Partial methods jsem si pracovně přeložil jako "roztrhané metody". Jedná se o metodu (pouze sub a pouze private), která má oddělenou definici a deklaraci. Tento přístup je znám zejména z jazyka C++, který vyžaduje aby deklarace metody vždy předcházela jejímu použití. Ve Visual Basicu toto nutné není, ale může se to hodit ve chvíli, kdy definici (tedy tu část s kódem) generuje nějaký generátor a tak není dostupná v době psaní kódu, což generuje chybové hlášky a metoda chybí v IntelliSensu. To vás může vést k tomu, že si metodu v jedné části partial třídy deklarujete a v jiné se pak "sama" definuje (ale jde to i v rámci jedné části partial třídy). Partial metody můžete používat i v rámci struktur a modulů. U modulů to, ale asi moc smysl nemá, protože modul jako takový se nemůže skládat z více částí.

Vypadat to bude asi takto:

Class [MyClass]
    Private Partial Sub MySub() 'Deklarace
    End Sub
'------------------------------------------------
    Private Sub MySub() 'Definice
        'Kód
    End Sub    
End Class

Je poněkud podivné, že na rozdíl od MustOverridemetod, deklarace partial metod musí obsahovat i End Sub(a prázdné tělo), což poněkud snižuje přehlednost kódu. To že partial metody mohou být jen private subje ještě více omezující. Bylo by jistě zajímavé, kdyby to mohly být i funkce a vlastnosti (případně custom události) a nemusely být private.

A teď ta největší podivnost: Co si myslíte, že se stane, když zapomenete k deklaraci roztržené metody dopsat její definici (tělo, kód)??? Chyba při kompilaci? Ne! Nestane se nic! Vůbec nic! Metoda se dokonce ani neobjeví ve zkompilované assembly jako prázdná. Prostě tam vůbec nebude! A co se stane s voláním těchto metod? Zmizí! Vůbec se nezkompilují! Pokud výraz, kterým se vypočítával parametr předávaný metodě měl nějaký vedlejší efekt, tak k tomuto vedlejšímu efektu nedojde. Výraz ve výsledné assembly prostě nebude. Pokud se pokusíte na roztrženou metodu zavolat AddressOf, tak vám to projde jen v případě, že existuje definice. Pokud definice neexistuje, je to chyba při kompilaci (která se ve VS ihned modře podtrhne). Zajímalo b y mne jestli toto chování, je to co autoři chtěli, nebo se to do finální verze ještě změní. Zkusím pogooglit a dám vědět.

3. Inicializace členů objektů při vytváření instancí

Jedná se o velmi podobnou vlastnost, která je ve VB8 k dispozici pro inicializaci atributů aplikovaných na nějaké položky. Lehkou záhadou zůstává, proč nebyla použita stejná syntaxe jako u atributů. Asi proto, abyste v konstruktorech "neatributů" mohli používat pojmenované parametry.

Podívejte se na následující kód:

Public Class MyCls : Inherits Attribute
    Public Sub New()
    End Sub
    Public Sub New(ByVal RW As String)
    End Sub
    Public Property RW() As String
        Get
        End Get
        Set(ByVal value As String)
        End Set
    End Property
    Public ReadOnly Property RO() As String
        Get
        End Get
    End Property
    Public WriteOnly Property WO() As String
        Set(ByVal value As String)
        End Set
    End Property
    Public ReadOnly ROf As Long
    Public RWf As Long
End Class
<MyCls(RW:="RW", RWf:=11L, WO:="WO")> _
Public Module M
    Sub m()
        Dim x = New MyCls("RW") With {.RW = "4", .WO = "A", .RWf = 4L}
    End Sub
End Module

Na před-předposledním řádku vidíte inicializaci hodnot dostupných vlastností a proměnných objektu typu MyCls. To, že používám konstruktor, který vypadá jako, že nastavuje hodnotu vlastnosti RW, vůbec nevadí, v inicializaci jí mohu nastavit znovu, na co chci jiného. Třídu MyClsjsem záměrně oddědil od Attribute, abych mohl ukázat rozdíl oproti inicializaci atributů. Tam není na první pohled zřejmé, jestli se použije konstruktor bez parametrů a nastaví se vlastnost RW, nebo se použije konstruktor s jedním parametrem, který se jmenuje také RWa vlastnost RWse nenastaví. Skutečnost je taková, že se použije konstruktor bez parametrů. Pokud chcete použít konstruktor s parametrem, musíte zadat jeho parametry pozičně (<MyCls("RW", RW:="RW")>). V inicializaci atributu nelze stejně jako ve VB8 používat pojmenované argumenty (a nefunguje tam doplňování názvů vlastností - IntelliSense). Při "obyčejné" ("nové") inicializaci, pojmenované argumenty klidně použít můžete:

Dim x = New MyCls(RW:="RW") With {.RW = "4"}

Tato nová fičura vám jistě usnadní pasní kódu, ale nemusela by být až tak "ukecaná" (C# vynechává Witha tečky před názvy členů). Škoda je, že tento způsob nelze použít pro inicializaci kolekcí (třeba List(Of T)) jako v C#, ale jen polí.

4. Local type inferencing (automatické odvozování typů lokálních proměnných)

Znáte to, deklarujete proměnnou Dim x = "Ahoj"a pokud nemáte projekt nastavený prasácky, hned na vás vyskočí warning Variable declaration without an 'As' clausule; type of Object assumed.. Mě osobně to ještě otravnější připadá u konstant. Což o to, u některých jednoduchých typů si pomohu typovým znakem Dim x$ = "Ahoj". Ale typových znaků je sakra málo, a tak je nutné dopsat 'As' klauzuli. Přitom každému trotlovi je i bez ní jasné, že je to String! Takže i VB9 přestal být trotl a nyní, když deklarujete proměnnou (konstantu) Dim x = "Ahoj", pochopí, že je typu String.

Ale nic není tak jednoduché, jak by mohlo být :-(. Podívejte se na následující ukázku a sledujte typy napsané v komentářích:

Module Modul
    Public Const C1 = 4L 'Long
    Public Const C2 = 4US 'UShort
    Public Const C3 = CByte(11)  'Byte
    Public Const C4 = "Ahoj" 'String
    Public Const C5 = "Đ"c 'Char
    Public Const C6 = Nothing 'Object
    Public Const C7 = AscW("Đ"c) 'Integer
    Public Const C8 = #10/10/2007# 'Date
    Public Const C9 = CType(11, SByte) 'SByte
    Public Const CA = CType(Nothing, String) 'String
    Public Const CB = CType(Nothing, List(Of Long)) 'Object
    Public Const CD As List(Of Long) = Nothing '<chyba - toto nelze>

    Private F1 = 4L 'Object
    Public F2 = New List(Of Integer)(New Integer() {1, 2}) 'Object
    Public F3 = "Ahoj" 'Object
    Public F4 = "Ahoj" + " " + "světe".ToString 'Object
    Public F5 = New TimeSpan(10, 10, 0) - New TimeSpan(10, 9, 0) 'Object

    Sub Test()
        Const C1 = 4L 'Long 
        Const CB = CType(Nothing, List(Of Long)) 'Object

        Dim F1 = 4L 'Long
        Dim F2 = New List(Of Integer)(New Integer() {1, 2}) 'List(Of Integer)
        Dim F3 = "Ahoj" 'String
        Dim F4 = "Ahoj" + " " + "světe".ToString 'String
        Dim F5 = New TimeSpan(10, 10, 0) - New TimeSpan(10, 9, 0) 'TimeSpan
        Dim F6 = CInt(CB) + C1 'Integer
    End Sub
End Module

Vidíte, že chování této fičury je poněkud inkonzistentní. Funguje pro jakékoliv výrazy ale jen pro lokální proměnné, a konstanty (lokální i globální) V originále [1] se tato fičura dokonce jmenuje local type inferencing. Z toho by vyplývalo, že bude fungovat jen pro lokální proměnné a konstanty. Ona funguje i pro nelokální konstanty, ale nefunguje pro nelokální proměnné (fields). Další záhada VB9 Beta 2.

5. Anonymní typy

Anonymní typy jsou typy, které nemají jméno. Jedná se opět o fičuru, která je přítomna i v C# a má především zjednodušit zápis kódu. Proměnná anonymního typu může být jen lokální a vznikne podobně jako při inicializaci členů, akorát vynecháte název typu.
Dim CementaryCustomer = New With { .Name = "Karel Barel", .Age = 110}

Definice říká, že proměnná anonymního typu je typu třídy, která nemá žádné jméno. Tato třída dědí přímo od Objectu. Typ vlastností je odvozen stejně jako u lokálních proměnných tato třída předefinovává metodu ToString(), tak aby vracela výraz, kterým byl anonymní objekt vytvořen. Zajímavé. Zvláště to o tom ToString. Takže se na to raději podíváme v praxi. Následujíc kód nám pomůže zjistit, co se za tím skrývá:

Dim Simple = New With {.Jméno = "Karel Barel", .Věk = 110}
Dim NotSoSimple = New With {.Jméno = "James" & " " & "Bond", .věk = CInt(4 * Math.PI)}
Dim Difficult = New With { _
        .Jméno = (New String() {"Pepa ZDepa", "Olgoj Chorchoj"})(1), _
        .věk = New Random(14).Next(1, 177)}
Console.WriteLine("Simple {0}; {1}", Simple, Simple.GetType.FullName)
Console.WriteLine("NotSoSimple {0}; {1}", NotSoSimple, NotSoSimple.GetType.FullName)
Console.WriteLine("Difficult {0}; {1}", Difficult, Difficult.GetType.FullName)

Console.WriteLine(Simple.GetType.Equals(Difficult.GetType))

Výstupem je něco takovéhoto:

Simple { Jméno = Karel Barel, Věk = 110 }; ...
NotSoSimple { Jméno = James Bond, Věk = 13 }; ...
Difficult { Jméno = Olgoj Chorchoj, Věk = 8 }; ...
True

Jako typ (...) se pokaždé vypsalo to samé: VB$AnonymousType_0`2[[System.String,mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]. To je dobrá správa, protože nám to říká, že stejně definované anonymní typy jsou to samé. Předpona VB nás ale varuje, že C#, tam dá asi jinou předponu ;-). Například f__. Pokud anonymní typ definujete s jinými názvy vlastností, také se nebude jednat o ten samý, ale například o VB$AnonymousType_1. To, že s jinými názvy vlastností se jedná o různé typy a se stejnými o stejné (pokud jsou vlastnosti stejného typu), ale platí jen v rámci jedné assembly! Číslo záleží na pořadí kompilace typů v rámci assembly. To znamená, že pokud byste porovnávali více anonymních typů z různých assembly, tak můžete zjistit, že stejné jméno obdržely typy s různými názvy vlastností (a různé se stejnými).

Výpis hodnot vlastností anonymního typu používá metodu ToStringaktuálního typu vlastnosti.

Poslední poznámka k této variantě anonymních typů: Při číslování je Visual Basic case insensitive a velikost písmen vlastností je odvozena z prvního kompilovaného typu.

Anonymní typy s klíčem

U anonymních typů popsaných výše jsou jejich vlastnosti Read-Write. Pokud však před název vlastnosti dáte klíčové slovo Key, bude taková vlastnost jen pro čtení.

Dim a = New With{Key .Prp = 209}

Jako odměnu za to, že vlastnost je jen read-only, VB tuto vlastnost začne chápat jako identifikátor (klíč; odtud Key) instance typu a takto deklarovaný anonymní typ automaticky implementuje System.IEquatable(Of T)a předefinovává Equals(typově bezpečnou i nebezpečnou) a GetHashCode.

Klíčů můžete mít i více.

Závěr k anonymním typům

Na závěr musím dodat, že anonymní typy jsou plně řešeny již při kompilaci, takže jejich používání nemá žádný negativní dopad na výkonnost aplikace.

A na úplný závěr jsem se podíval na anonymní typy disassemblerem v Reflectoru, abych zjistil jak opravdu vypadají. Povšimněte si, že názvy některých věcí nesplňují kritéria pro názvy, takže se k nim není možno dostat jinak než přes reflection.

Bez klíče

<DebuggerDisplay("\{ Jm" & ChrW(233) & "na = {Jm" & ChrW(233) & "na}, v" & ChrW(283) & "ky = {v" & ChrW(283) & "ky} \}", Type:="<anonymous type>"), CompilerGenerated> _
Friend Class VB$AnonymousType_1(Of T0, T1)
    ' Methods
    <DebuggerNonUserCode> _
    Public Sub New(ByVal Jména As T0, ByVal věky As T1)
        Me.$Jména = Jména
        Me.$věky = věky
    End Sub

    <DebuggerNonUserCode> _
    Public Overrides Function ToString() As String
        Return ("{ " & String.Format("{0} = {1}, ", "Jm" & ChrW(233) & "na", Me.$Jména) & String.Format("{0} = {1} ", "v" & ChrW(283) & "ky", Me.$věky) & "}")
    End Function

    ' Properties
    Public Property Jména As T0
        <DebuggerNonUserCode> Get
            Return Me.$Jména
        End Get
        <DebuggerNonUserCode> Set(ByVal Value As T0)
            Me.$Jména = Value
        End Set
    End Property

    Public Property věky As T1
        <DebuggerNonUserCode> Get
            Return Me.$věky
        End Get
        <DebuggerNonUserCode> Set(ByVal Value As T1)
            Me.$věky = Value
        End Set
    End Property

    ' Fields
    Private $Jména As T0
    Private $věky As T1
End Class

S klíčem (resp. se 2 klíči)

<DebuggerDisplay("\{ jm" & ChrW(233) & "no = {jm" & ChrW(233) & "no}, v" & ChrW(283) & "k = {v" & ChrW(283) & "k} \}", Type:="<anonymous type>"), CompilerGenerated> _
Friend Class VB$AnonymousType_2(Of T0, T1)
    Implements IEquatable(Of VB$AnonymousType_2(Of T0, T1))
    ' Methods
    <DebuggerNonUserCode> _
    Public Sub New(ByVal jméno As T0, ByVal věk As T1)
        Me.$jméno = jméno
        Me.$věk = věk
    End Sub

    <DebuggerNonUserCode> _
    Public Function Equals(ByVal val As VB$AnonymousType_2(Of T0, T1)) As Boolean Implements IEquatable(Of VB$AnonymousType_2(Of T0, T1)).Equals
        If (val Is Nothing) Then
            Return False
        End If
        Dim e_ As VB$AnonymousType_2(Of T0, T1) = val
        If (Me.$jméno Is Nothing) Then
            If (Not e_.$jméno Is Nothing) Then
                Return False
            End If
        ElseIf (e_.$jméno Is Nothing) Then
            Return False
        End If
        If (IIf((((Not Me.$jméno Is Nothing) AndAlso (Not e_.$jméno Is Nothing)) AndAlso Not Me.$jméno.Equals(e_.$jméno)), 1, 0) <> 0) Then
            Return False
        End If
        If (Me.$věk Is Nothing) Then
            If (Not e_.$věk Is Nothing) Then
                Return False
            End If
        ElseIf (e_.$věk Is Nothing) Then
            Return False
        End If
        If (IIf((((Not Me.$věk Is Nothing) AndAlso (Not e_.$věk Is Nothing)) AndAlso Not Me.$věk.Equals(e_.$věk)), 1, 0) <> 0) Then
            Return False
        End If
        e_ = Nothing
        Return True
    End Function

    <DebuggerNonUserCode> _
    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        Return Me.Equals(TryCast(obj,VB$AnonymousType_2(Of T0, T1)))
    End Function

    <DebuggerNonUserCode> _
    Public Overrides Function GetHashCode() As Integer
        Dim num As Integer = &H2BE025C7
        num = (num * -1521134295)
        If (Not Me.$jméno Is Nothing) Then
            num = (num + Me.$jméno.GetHashCode)
        End If
        num = (num * -1521134295)
        If (Not Me.$věk Is Nothing) Then
            num = (num + Me.$věk.GetHashCode)
        End If
        Return num
    End Function

    <DebuggerNonUserCode> _
    Public Overrides Function ToString() As String
        Return ("{ " & String.Format("{0} = {1}, ", "jm" & ChrW(233) & "no", Me.$jméno) & String.Format("{0} = {1} ", "v" & ChrW(283) & "k", Me.$věk) & "}")
    End Function

    ' Properties
    Public ReadOnly Property jméno As T0
        <DebuggerNonUserCode> Get
            Return Me.$jméno
        End Get
    End Property

    Public ReadOnly Property věk As T1
        <DebuggerNonUserCode> Get
            Return Me.$věk
        End Get
    End Property

    ' Fields
    Private ReadOnly $jméno As T0
    Private ReadOnly $věk As T1
End Class

6. Lamba expressions and closures

6. Lambda (λ) expressions

Lambda metoda je anonymní (bezejmenná) metoda, která umožňuje "nacpat" in-line kód kamkoliv, kde je akceptován delegát.[1] Její deklarace vypadá asi takto:

Dim f = Function(x As Integer) x + 1

Význam tohoto je něco jako:

Delegate Function df(x As Integer) As Integer
Private Function ff(x As Integer) As Integer
    Return x + 1
End Function
Sub xxx()
    Dim f As df = AddressOf ff
End Sub

Jak sami musíte uznat, ušetří to kvantus kódu, který by bylo nutné napsat pro vytvoření něčeho tak trapného jako funkce přičítající jedničku! Přitom takto trapné funkce jsou poměrně často potřeba - posílají se do různých porovnávacích rutin (na příklad).

Lambda expression nemůže mít optional (volitelné) parametry ani parametry typu ParamArray. ByVali ByRefje povoleno.

Zajímavé jsou možnosti neurčování typů pro lambdy. Nejen že nelze nijak natvrdo určit návratový typ lambda funkce, ale též lze neurčit typ parametru a přesto bude znám.

Dim f = Function(x) x + 1 'Warning Variable declaration without ... a ještě jeden u '+'
Dim f As dInt = Function(x) x + 1 'Předpokládejme Delegate Function dInt(ByVal x As Integer) As Integer, pak je vše v pořádku a typ x je znám

Lambda funkce je možné deklarovat i mimo procedury (přiřazovat je k instančním/třídním proměnným typu delegát). Lambda funkce mají přístup k instanci ve které byly deklarovány (např. k privátním proměnným) a pokud jsou deklarovány v rámci metody mají přístup i k lokálním proměnným této metody.

Class n
    Private g As Long
    Sub ň()
        Dim h As Integer
        Dim t = Function() g + h
    End Sub
End Class

Closures

Klozůry - opravdu mě nenapadá jak to přeložit ;-)

Jak již bylo zmíněno, lambda výrazy mají přístup k lokálním proměnným funkcí. Jenomže lambda výraz může žít déle než funkce, k jejímž lokálním proměnným má výraz přístup. Z toho vyplívá potřeba neumisťovat takovéto lokální proměnné na zásobník ale na haldu (heap). Za tímto účelem vytvoří kompilátor třídu, do které uloží lokální proměnné, které lambda používá a také referenci na instanci třídy, ke které má mít lambda přístup (pokud to vyžaduje).

Používání "klozůr" koliduje s použitím příkazů skoku a proto jsou v některých případech zakázány. "Klozůra" totiž obdrží hodnoty lokálních proměnných ve chvíli, kdy kód vstoupí do bloku, kde je deklarována. Z toho důvodu není možné provádět skok to takovéhoto bloku. (Ale kdo dnes používá GoToa Resume!?)

Abyste si náhodou z předchozího tvrzení nemysleli, že "klozůra" obdrží kopii lokální proměnné, tak věřte, že následující kód vypíše 102.

Dim h As Integer = 11
Dim t = Function() h
h = 102
Console.Write(t.Invoke)

7. Expression trees

Expression tree (strom výrazu) je kód, který nebyl přeložen do binární formy (MSIL), ale namísto toho byl přeložen do stromu, který určuje strukturu výrazu. Takto uložený výraz lze pak přeložit do jiného jazyka (např. SQL). Stromy výrazů (nazýváno též "kód jako data") se používají hlavně při LINQu oproti nějakému externímu zdroji dat - například SQL serveru. Stromy výrazů jsou podobné CodeDOM reprezentaci programu, ale obsahují jen výraz, nikoli celý program. I když kdo ví. Jazyk F# umožňuje uložit tak i větší bloky kódu. Třeba se někdy dočkáme doby, kdy půjde Visual Basic "zkompilovat" do JavaScriptu nebo do T-SQL uložené procedury...

Ve VB máme nějaké výrazy, které mají nějaký typ, něco znamenají a něco dělají. V průběhu jejich vyhodnocování dochází k tzv. reklasifikaci. Například přístup k lokální proměnné je nahrazen (reklasifikován na) její hodnotou, volání funkce návratovou hodnotou atp. Speciálním případem této reklasifikace je reklasifikace labmda výrazu. Ten může být jednak reklasifikován na parametr konstruktoru delegáta (jak bylo ukázáno výše), ale pokud jeho cílovým typem je System.Linq.Expressions.Expression(Of T)a Tje delegát, tak lambda výraz je interpretován, jako by byl použit při konstrukci delegáta Ta pak je konvertován na strom výrazu. Specifikace [1] nespecifikuje, jak přesně se budou výrazy do stromu výrazů překládat :-( a negarantuje, že to bude konzistetní mezi jednotlivými budoucími verzemi VB.

8. Relaxed delegates

Jedná se o větší flexibilitu při používání delegátů. Jsou možná některá přiřazení, která dříve možná nebyla.

Podívejte se co vše VB9 umožňuje. V komentářích je uvedeno jaký názor na to měl VB8.

Delegate Sub D1(ByVal a As Integer)
Delegate Sub D2(ByVal a As Long)
Delegate Sub D3(ByVal a As Integer, ByVal b As Object)

Private Sub S1(ByVal a As Integer)
End Sub
Private Sub S2(ByVal a As Long)
End Sub
Private Sub S3(ByVal a As Integer, Optional ByVal b As Object = Nothing)
End Sub

Private Sub Test()
    Dim I1 As D2 = AddressOf S1 'Does no have same signature
    Dim I2 As D1 = AddressOf S2 'Does no have same signature
    Dim I3 As D1 = AddressOf S3 'Does no have same signature
End Sub

Private Event E1 As D1
Private Event E2 As D2
Private Event E3 As D3

Private Sub H1(ByVal a As Integer) Handles Me.E2 'Do not have same signature
End Sub
Private Sub H2(ByVal a As Long) Handles Me.E1 'Do not have same signature
End Sub
Private Sub H3(ByVal a As Long, Optional ByVal b As Object = Nothing) Handles Me.E3 'Do not have same signature
End Sub

Private Event MouseMove As EventHandler(Of System.Windows.Forms.MouseEventArgs)
Private Sub OnMouseMove(ByVal Sender As System.Windows.Forms.Button, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles Me.MouseMove 'Do not have same signature
End Sub

9. Nativní podpora pro nulovatlené typy (nullable types)

Nulovatelné typy byly již v .NET frameworku 2.0. VB9 ale zjednodušuje práci s nimi. To co jste dříve museli deklarovat jako Nullable(Of T)dnes deklarujete jako T?. Jedná se pouze o syntaktickou zkratku, takže platí všechno co platilo pro Nullabledoposud. Především to, že nulovatelné mohou být jen struktury. Kód napsaný starým a novým způsobem je plně zaměnitelný.

VB9 kromě této syntaktické zkratky přidává ještě další zjednodušení.

  • Není dosud implementováno: Ke všem členům typu Tmůžete přistupovat přímo z T?
  • Automatická konverze ze základního do nulovatelného typu a naopak.
  • Odvozené konverze. Pokud typ Tmá konverzi do S, tak T?také. T?má pak i konverzi do S?. Pokud Smá konverzi do T, pak Smá i konverzi do T?a S?do T?. Konverze z/do S?jsou samozřejmě možné jen pokud Sje struktura. Pokud Sje třída a T?.HasValueje false, tak se T?na Spřetypuje jako Nothing.
  • Dvojí deklarace jako u polí. Dim a As Long?a Dim a? As Long. Dim a? As Long?je chyba, stejně jako u polí.

10. Podmíněný (ternární) operátor

Nikde jsem o tom doposud neslyšel. Nikdo to halasně neprezentoval. A přitom je to největší změna ve Visual Basicu od jeho vzniku!!! :-) VB programátoři konečně dostávají do ruky ternární operátor! Ale nebyl by to Visual Basic, kdyby se jednalo o operátor ?:. Operátor je realizován velmi podobně jako nechvalně známá funkce Iif, ale jmenuje se If. Má 2 podoby. Se třemi a dvěma argumenty.

První podoba

If(Contidion, TruePart, FalsePart)

je klasický ternární operátor, který při splnění podmínky vyhodnotí TrueParta při nesplnění FalsePart. Narozdíl od Iifa podobně jako u AndAlsoa OrElsemůžete používat i výrazy, které při splnění/nesplnění podmínky skončí chybou. Vyhodnotí se jen ta část, která se vyhodnotit musí, takže k chybě nedojde.

Druhá podoba

If(Object1, Object2)

slouží k ošetření nullových hodnot. Pokud Object1je Nothingvrací Object2, jinak vrací Object1. Funguje i pro Nullablea druhou část vyhodnocuje jen když musí. Je to zkratka k If(Object1 Is Nothing, Object1, Object2), ale není jí plně ekvivalentní. Výraz na místě Object1 je vyhodnocen jen 1×. A pokud Object1je T?a Object2je "bez otazníku" a typu struktura, tak při rozhodování o typu návratové hodnoty se Object1chápe jako T.

Jak již tedy vyplynulo, Ifje na rozdíl od Iiftypově bezpečný (v obou variantách).

Zajímavostí je, že If(x, Nothing, Nothing)by mělo způsobit chybu při kompilaci. V praxi jsem však ověřil, že to takto není (zatím) implementováno.

11. LINQ

LINQ = Language Integrated Query (dotaz integrovaný do jazyka) umožňuje vykonávat deklarativně zapsané (SQL-like) dotazy jednak přes jakékoliv kolekce v .NETu a také přes datové zdroje které umí interpretovat stromy výrazů. LINQ je poměrně rozsáhlé téma, takže podrobný popis si necháme na jindy. Nejdřív ho musim já sám pochopit ;-).

12. InternalsVisibleTo

System.Runtime.CompilerServices.InternalsVisibleToAttributeje atribut, který lze aplikovat na úrovni assembly a inicializovat jej na název assembly, které získá přístup ke všem elementům z assembly, na níž je aplikován, a které jsou deklarovány jako Friend. Název assembly, pro kterou garantujete přístup může být buď silný (s verzí a tokenem) nebo slabý (jen název). Atribut můžete použít několikrát a garantovat tak přístup více spřáteleným sestavením (friend assemblies).

To se hodí v případě, kdy chcete například velkou knihovnu rozdělit do několika assembly a jednotlivým assembly dát přístup ke členům, ke kterým ale nemá mít přístup uživatel knihovny. Také se to může hodit, pokud programujete nějaké testy pro vaši knihovnu a tyto testy nají mít přístup k neveřejným položkám knihovny. VB9 nově tento atribut rozpoznává a validuje jej již v době návrhu.

Assembly Project1

<Assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Project2")>
Friend Module M()
    Public Sub S()
    End Sub
End Module

Assembly Project2

Module M2
    Public Sub Main()
        Project1.M.S()
    End Sub
End Module

Assembly Project2má přístup k Friendprvkům z assembly Project1.

13. Rozšířené skupinové třídy

Skupinové třídy jsou vlastnost kompilátoru, kterou většinou využívá jen on sám. Ale můžete ji použít také, pokud k tomu najdete nějaký důvod. Tímto způsobem je možno zpřístupnit defaultní instance objektů - tak jako máte přístup k defaultní instanci všech formulářů ve WinForms aplikaci.

Defaultní instance formulářů jsou Visual Basicem vyráběny takto:

<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", "Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
    Private Shared Function Create(Of T As {New, Form}) _
        (Instance As T) As T
        If Instance Is Nothing Then
            Return New T()
        Else
            Return Instance
        End If
    End Function

    Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
        Instance.Close()
        Instance = Nothing
    End Sub
End Class

Uvedený kód zkompiluje takto:

<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", "Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
    Private Shared Function Create(Of T As {New, Form})(Instance As T) As T
        If Instance Is Nothing Then
            Return New T()
        Else
            Return Instance
        End If
    End Function

    Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
        Instance.Close()
        Instance = Nothing
    End Sub

    Private m_Form1 As Form1

    Public Property Form1() As Form1
        <DebuggerNonUserCode> Get
            Return Create(m_Form1)
        End Get
        <DebuggerNonUserCode> Set (Value As Form1)
            If Value IsNot Nothing AndAlso Value IsNot m_Form1 Then
                Throw New ArgumentException("Property can only be set to Nothing.")
            End If
            Dispose(m_Form1)
        End Set
    End Property
End Class

Kompilátor do třídy označené atributem MyGroupCollectionAttributeautomaticky pro všechny třídy v projektu, které dědí od Formdoplní vlastnosti pro získání jejich defaultních instancí. Defaultní instance jsou inicializovány metodou Createa destruovány metodou Disposea jsou dostupné přes defaultní instanci třídy MyForms, která je dostupná přes My.Forms. Pokud to budete chtít použít samy, tak poslední parametr konstruktoru atributu je ignorován. Defaultní instanci třídy, která defaultní instance zpřístupňuje, si musíte vytvořit samy.

Tato vlastnost byla dostupná již ve VB8. VB9 ji rozšiřuje o možnost zadat do prvních 3 parametrů konstruktoru atributu čárkou oddělený seznam. Počty položek v čárkou oddělených seznamech u jednotlivých parametrů musí souhlasit. Jejich význam je takový, že do dané třídy jsou vytvořeny vlastnosti vracející výchozí instance všech typů, které dědí od typů uvedených v čárkou odděleném seznamu prvního parametru. Tyto typy jsou inicializovány metodami uvedenými na odpovídajících pozicích čárkou odděleného seznamu u druhého parametru a destruovány metodami od třetího parametru. VB9 také přidává podporu pro generické typy - tedy jen pro negenerické typy, které dědí od generických typů.

14. Interface "dědí" od objectu

Pokud deklarujete proměnnou typu interface, máte u takovéto proměnné přístup ke všem metodám typu Object. To ve VB8 nebylo. Pokud v interfacu deklarujete něco, co se jmenuje stejně jako metoda třídy Object, je metoda třídy Objectimplicitně zastíněna (shadowed) (a nelze tomu nijak zabránit).

15. Integrované XML

Tato vlastnost, nevím proč se o ní Beta 2 dokumentace [1] nezmiňuje (informace najdete třeba na [2]), vám umožňuje integrovat XML přímo do zdrojového kódu Visual Basicu. Můžete deklarovat proměnnou typu System.Xml.Linq.XDocumenta přiřadit jí XML dokument. Jedná se o opravdový XML dokument, žádný String, žádné uvozovky. Můžete vytvořit ještě literály typu XProcessingInstruction, XElement, XCommenta XCdata. Atributy a textové uzly nejsou možné.

Ke XML ve Visual Basicu se váže ještě jedna věc. A to příkaz Import, který nově umožňuje importovat jmenný prostor XML. A to jak defaultní tak pojmenovaný.

Imports <xmlns:html="http://www.w3.org/1999/xhtml">
Dim i = <?xml version="1.0" encoding="utf-8"?>
        <html:html>
            <html:head>
                <html:title>Pokus</html:title>
            </html:head>
            <html:body>
                <html:p>Pokus</html:p>
            </html:body>
         <html:html>
         <?test test?> 'XDocument
Dim a = <?ahoj?> 'XProcessingInstruction
Dim b = <?xml version="1.0"><root/> 'XDocument
Dim c = <ahoj/> 'XElement
Dim d = <nazdar><bazar/></nazdar> 'XElement
Dim k = <--koment--> 'XComment
Dim cd = <![CDATA[Test]]> 'XCData
Dim err = <?xml version="1.0"> 'Chyba, očekáván root
        

Jak jste si jistě všimli, Visual Basic nevyžaduje v XML na konci řádku podtržítko. Visual Basic sám pozná, kde XML končí, když uzavřete první element. Za uzavření nejvnějšnějšího elementu vám dovolí umístit ještě procesní instrukce (jak ukazuje HTML příklad; platí jen pro XDocument). Určení verze XML 1.0je povinné a musí být 1.0. Editor vám sám dělá odsazení uvnitř XML a kontroluje zda je well-formed. Intellisense mi nefungoval, ale nápověda [2] jej slibuje, pokud si přidáte příslušný XSD dokument do projektu. Nevím jak to bude s validací XML oproti schématu. Na DTD zapomeňte. Tato zastaralá technologie není podporována a do integrovaného XML ani nemůžete zapsat DOCTYPE.

Editor za vás dělá ještě tu zajímavou věc, že když změníte název otevíracího tagu, změní sám název uzavíracího. Pokud změníte název uzavíracího, vrátí jej zpět.

Visual Basicové komentáře (uvozené apostrofem ') můžete dávat jen za konec XML. Pokud do XML zadáte podtržítko (jako pokračování řádku) nebo apostrof (jako komentář), budou se brát jako textové uzly XML a stanou se jeho součástí.

A protože XML je objekt, můžete udělat například toto:

Dim h = <?xml version="1.0"><root/>.Document

S proměnnými typu XDocument a XElement můžete dělat další psí kusy (a tam již Intellisense funguje). Pro následující příklad si přestavte proměnnou i z prvního XML příkladu, ale s atributem u hlavičky navíc <html:head id="myh">:

Dim heads = i...<html:head> 'Všichni potomci typu html:head
Dim hid = i...<html:head>.@id 'Atribut id elementu head ("myh")

Visual Basic tedy definuje vlastnosti pro XML osy.

osa vlastnost použití
attribute @ object.@attribute
object.<attribute>
child . object.<child>
descendant ... object...<descendant>

Další osy (jako parent) jsou dostupné textově (přes vlastnosti a metody XML objektů). Ve Visual Basicu tedy máte něco jako XPath.

Visual Basic také umožňuje XML konstruovat. Zde se protínají světy XML a LINQ. V nápovědě [2] najdete následující ukázku:

Dim phones As XElement = _
    <phones>
        <phone type="home">206-555-0144</phone>
         <phone type="work">425-555-0145</phone>
     </phones>

Dim phoneTypes As XElement = _
    <phoneTypes>
        <%= From phone In phones.<phone> _
            Select <type><%= phone.@type %></type> _
        %>
    </phoneTypes>

Console.WriteLine(phoneTypes)

Jejím výsledkem je:

<phoneTypes>
    <type>home</type>
    <type>work</type>
</phoneTypes>

Jak vidno XML ještě v kombinaci s LINQ bude ve VB mocným nástrojem. Takže se na něj třeba někdy podíváme podrobněji.

16. Další změny

Jen ve stručnosti zmíním další změny, na které jsem narazil a které nejsou ani tak změnami jazyka jako změnami jeho integrace s IDE Visual Studia.

Vylepšený intellisense

Intellisense nyní ve Visual Basicu zobrazuje i klíčová slova a poskytuje vám tak kompletní nápovědu při psaní kódu.

XML dokumentace

Se SP1 pro VS 2005 přišla pro programátory ve Visual Basicu nepříjemná změna: Generované soubory s XML komentáři neobsahovali korektní informace o typu v atributech crefelementů jako <see>nebo <exception>. Tím bylo znemožněno procházení XML dokumentace a použití nástrojů jako SandCastle. Tato chyba byla konečně (!) opravena.

Multitargeting

Multitargeting je nová vlastnost Visual Studia 2008 (težko říci jestli se jedná o vlastnost Visual Studia nebo jazyka; takový Phalanger nebo IronPython ji podporoval již dávno), která umožňuje vybrat si cílovou platformu, pro kterou se má kód buildovat. Výběr platformy souvisí s verzí kompilátoru vbc, která se použije. Na nižších verzích platforem pak nemáte k dispozici některé třídy a rovněž některé vlastnosti jazyka, které byly přidány až později. Toto si můžete ošetřit například podmíněným překladem, kde přibyla nová konstanta #Const VBC_VER.

17. Závěr

Pokusil jsem se shrnout všechny změny jazyka Visual Basic 9 oproti verzi 8 (2005 -> 2008). Jsem si vědom toho, že jsem poněkud "odflákl" LINQ a XML. Tento článek je i tak poměrně dlouhý a mě to dává prostor pro napsání ještě dalších dvou článků ;-). Také jsem letmo zmínil novinky v integraci s Visual Studiem. Ty by si ale také zasloužili ještě podrobnější studium. Při zkoušení nových vlastností jazyka jsem narazil na některé "zvláštnosti", o kterých si nejsem jist jestli jsou záměrem nebo bugem beta verze. Uvidíme časem.

Visual Basic 9 je jistě krok dobrým směrem a posouvá programování zase o kus dál. V porovnání s jazykem C# se VB drží docela dobře. Má některé vlastnosti navíc (automatické odvození typu, XML). Některé jiné by mohly být ve VB dotaženy k větší dokonalosti (inicializátory). Některé nemá vůbec (<Field: Attr>, automatické gettery a settery).

Rád bych ještě vyzval všechny, kdo si nainstalovali pre-release verzi Visual Studia (ale i budoucí uživatele stabilní verze), aby využili stránky Microsoft Connect a reportovali Microsoftu vše, co považují za bug nebo nedokonalost. Z vlastní zkušenosti mohu říci, že si to minimálně někdo přečte a rozhodne se jestli se tím bude nebo nebude zabývat a kdy. Můžeme tak pomoci udělat náš Visual Basic ještě lepším, až nejlepším!

Vivat Visual Basic
Đonny

Jan Záruba :: 23. října 2007 :: 1894 shlédnutí :: 2 komentářů
kategorie: Databáze a práce s XML, Obecná .Net témata, Vývoj webových aplikací, Vývoj Windows aplikací, Microsoft - produkty

Comments

By Jan Záruba @ 24. října 2007 17:31
Po odeslání článku, mi ještě v hlavě zůstalo pár nejasností a tak jsem trochu zapátral, abych si je ujasnil.
Svoje postřehy shrnuji v článku Co je nového ve Visual Basicu 9 :: Dodatek
By Jan Záruba @ 9. listopadu 2007 11:50
http://www.netstudent.cz/%C4%8Cl%C3%A1nky/tabid/56/articleType/ArticleView/articleId/100/Co-je-novho-ve-Visual-Basicu-9--Dodatek.aspx
Musíte být přihlášen pro posílání komentářů. Přihlásit se můžete zde
Přehled posledních diskuzí

Přehled posledních diskuzí

  1. ASP.NET programátor zlín [12.16.2008 10:17 odp.]
    Dobrý den,v současné době hledáme programátora na pozici: ASP.NET / C# programátor - Zlín Požadavky: schopnost sam...
  2. RE: MS Fest video [12.08.2008 6:20 odp.]
    AhojVideo záznam bude, po večerech pilně pracujeme na jeho stříhání. Po dokončení se zde určitě vyskytne link na stažení...
  3. MS Fest video [12.04.2008 4:18 odp.]
    Existuje videozáznam z MS Festu konaneho (koncem listopadu 2008) o vikendu na Male Strane na fakulty MFF? Je nekde mozne...
  4. Práce pro programátora [11.25.2008 10:12 dop.]
    Hledám nadšeného programátora na vytvoření webstránek online tv/stream video. V případě zájmu pište na email: projekt@tr...
Novinky z klubů

Novinky z klubů

  1. Programátorské večery: F# 15. prosince 2008
    Ve čtvrtek 18.12.2008 se v rámci programátorských večerů uskuteční přednáška n...
  2. Programátorské večery: VSTO 10. prosince 2008
    Nad kancelářským balíkem MS Office se dají tvořit rozsáhlé aplikace. Od jednod...
  3. Programátorské večery: .NET 4.0 3. prosince 2008
    Zajímá Vás, na co se můžete těšit v nové verzi .NETu? Pak se přijďte podívat n...
  4. Záznamy z programátorských večerů 3. prosince 2008
    Záznamy z prog. večerů (prezentace+dema, občas i nějaké to video) můžete dočasně najít na http://ci...
Co se píše jinde

Co se píše jinde

Windows 7 Beta k dispozici pro MSDN předplatitele

Windows 7 Beta k dispozici pro MSDN předplatitele

Silverlight 2 úvod

Zajímá vás psaní webových aplikací na straně klienta ve vašem oblíbeném nástroji v .NETu? Slyšeli jste o Silverlightu...

PF 2009

Co tak popřát čtenářům programátorského blogu do nového roku? Snad abyste dostávali rozumná zadání a nemuseli tenkou ...

TypeConvertery

V tomto díle si vysvětlíme, co jsou TypeConvertery a k čemu se používají. To si také ukážeme na příkladu.

Atributy jmenného prostoru System.ComponentModel

V tomto článku si ukážeme jak efektivně využívat atributy z jmenného prostoru System.ComponentModel při vytváření vla...