Integrované prostředí rubriky Visual Basic

Autor: Ján Hanák

Programátorská laboratoř

Prostor pro experimentování

Časová náročnost

(min):

75

Začátečník

Pokročilý

Profesionál

Použitý operační systém : Hlavní vývojový nástroj :

Další vývojový software :

Jiný software :

Windows 2000 SP3

Visual Basic .NET 2002

Žádný

Žádný

 

Nepřehlédněte exkluzivní Speciál pro programátory, který pojednává o jazykové interoperabilitě mezi jazyky Managed Extensions for C++ a Visual Basic .NET.

 

 

Tvorba zapečetěné třídy

Přístup k registrům operačního systému Windows

Vytváření vlastního delegáta

 

 

Tvorba zapečetěné třídy

 

V jistých situacích je pro programátora nežádoucí, aby třída, kterou vytvořil svými vlastními silami, byla zdrojem pro další odvozené třídy. Jestliže programátor použije v deklaraci třídy klíčové slovo NotInheritable, výslovně nařizuje, že daná třída nemůže sloužit jako bázová třída. Třídě s uvedenými charakteristikami se říká zapečetěná třída. Pokud porovnáme standardní třídu a její zapečetěnou kolegyni, můžeme pozorovat, že primárním rozdílem mezi těmito dvěma třídami je jejich chování při dědění.

 

Pod pojmem „standardní třída“ budeme v následujícím textu rozumět třídu, v deklaraci které se nenachází klíčové slovo NotInheritable, jakožto ani jiné modifikátory, které jakýmkoliv způsob ovlivňují chování třídy.

 

Zatímco od standardní třídy můžeme odvodit tolik podtříd, kolik budeme chtít, zapečetěná třída touto dovedností nedisponuje. Ovšem obě třídy mohou vytvářet své instance, v tomto směru se zapečetěná třída chová úplně stejně jako její standardní protějšek.

 

I když není tvorba zapečetěné třídy zcela běžnou záležitostí, existuje několik pádných důvodů pro označení třídy klíčovým slovem NotInheritable:

 

 

Zapečetěné třídy jsou zpravidla třídy, jejichž stavba plně pokrývá vyřešení jisté problematiky. Jednoduše řečeno, zapečetěná třída obsahuje všechny prvky (vlastnosti, metody a datové členy), které budou instance této třídy využívat. Zapečetěná třída je tak daleko objemnější a robustnější jako jakákoliv jiná třída. Tato skutečnost vyplývá právě z faktu, že zapečetěná třída si „veze všechno s sebou“. Abyste viděli hlavní rozdíly mezi návrhem a tvorbou standardní a zapečetěné třídy, uveďme si některé podstatné znaky (tab. 1).

 

Komparační znaky

Typ třídy

Standardní třída

Zapečetěná třída

Proces návrhu

Při návrhu standardní třídy je ve většině případů brán do úvahy požadavek na pozdější rozšiřitelnost třídy pomocí dědičnosti, a proto se standardní třída navrhuje jako bázová třída. To znamená, že do bázové třídy je zařazena pouze všeobecná, resp. základní funkcionalita, ovšem veškeré konkrétní nebo specifické rysy jsou implementovány až v odvozených třídách.

Proces návrhu zapečetěné třídy musí pozůstávat z pečlivé analýzy všech aspektů funkcionality této třídy. Protože charakter zapečetěné třídy nemůže být později rozšířen v podtřídách, musí zapečetěná třída obsahovat všechny členy, které budou instance této třídy ke své práci potřebovat.

Dědičnost

Odvozené třídy vznikají na základě techniky jednoduché dědičnosti, která je přímo podporována platformou .NET (přesněji společnou jazykovou specifikací – Common Language Specification). Standardní třída není v procesu dědění nijak omezena, a proto je možné vytvářet libovolné množství podtříd.  

Zapečetěná třída dědičnost nepodporuje, což znamená, že nemůže sloužit jako bázová třída pro tvorbu odvozených tříd.

Rozšíření

Rozšíření standardní třídy může v zásadě probíhat ve dvou směrech:

·          rozšíří se přímo programový kód standardní třídy

·          rozšíření se uskuteční upravením programového kódu odvozených tříd

Rozšíření zapečetěné třídy se musí uskutečnit prostřednictvím začlenění dodatečného programového kódu do samotné zapečetěné třídy.

Vytváření instancí

Plná podpora vytváření instancí třídy.*

Plná podpora vytváření instancí třídy.*

* Za předpokladu, že programátor nezačlenil do kódu třídy privátní konstruktor.

* Za předpokladu, že programátor nezačlenil do kódu třídy privátní konstruktor.

 

Tab. 1 – Komparace standardní a zapečetěné třídy

 

Na následujících řádcích je uveden výpis zdrojového kódu zapečetěné třídy:

 

Option Strict On

Public NotInheritable Class ZapečetěnáTřída

    Private m_Počet As Byte

 

    Public Property ZjistitPočetFormulářů() As Byte

        Get

            Return m_Počet

        End Get

        Set(ByVal Value As Byte)

            m_Počet = Value

        End Set

    End Property

 

    Public Sub VytvořitFormuláře(ByVal Počet As Byte)

        m_Počet = Počet

        If Počet <= 0 Or Počet > 10 Then

            MessageBox.Show("Bylo zadáno nevhodné číslo (" & Počet _

            & ") pro počet formulářů." & vbCrLf & _

            "Nyní bude zobrazen dialog, v němž budete moci zadat " & _

            "správné číslo pro počet formulářů.", "Zpráva o chybě", _

            MessageBoxButtons.OK, MessageBoxIcon.Error)

            Dim odp As Byte = CByte(InputBox _

            ("Zadejte číslo pro počet formulářů (<1, 10>).", _

            "Určení počtu formulářů", "1"))

            Počet = odp

            GoTo Tvorba_Formulářů

        Else

Tvorba_Formulářů:

            Dim a As Byte

            Dim frm(Počet - 1) As Form

            For a = 0 To CByte(Počet - 1)

                frm(a) = New Form()

                With frm(a)

                    .Text = "Formulář č. " & a + 1

                    .Show()

                End With

                Application.DoEvents()

            Next

        End If

    End Sub

End Class

 

Ukázková zapečetěná třída slouží na vytváření polí instancí třídy Form. Jak si můžete všimnout, v těle třídy se nachází veřejná metoda VytvořitFormuláře, která odvádí veškerou potřebnou práci. Počet vytvořených formulářů je uložen do soukromého datového členu s názvem m_Počet. Hodnotu členu m_Počet lze získat zavoláním veřejné vlastnosti ZjistitPočetFormulářů. Věřím, že jako profesionálním programátorům je vám kód zapečetěné třídy víceméně jasný, ovšem chtěl bych vás upozornit na tyto skutečnosti:

 

  1. Kód metody VytvořitFormuláře testuje hodnotu formálního parametru Počet (typu Byte). Jestliže je hodnota tohoto parametru jiná než jsou povolené hodnoty z intervalu <1, 10>, zobrazí se varovní hlášení. Hlášení informuje programátora, že byla zadána nepřípustná celočíselná hodnota. Jakmile bude zavřeno okno s varovným hlášením, bude zavolána funkce InputBox, která zobrazí dialogové okno s textovým polem pro zadání správné hodnoty pro počet formulářů, které se mají vytvořit. Zadaná hodnota bude explicitně konvertována do typu Byte a uložena do proměnné odp. Následně bude proveden příkaz GoTo, který přeskočí na specifikované návěstí (Tvorba_Formulářů).   

 

Všimněte si, že v kódu není proveden test návratové hodnoty funkce InputBox (jednoduše předpokládáme, že uživatel zadá správnou hodnotu z ohraničeného celočíselného intervalu). Pokud bude tento kód používat v reálných aplikacích, ujistěte se, že jste provedli také test návratové hodnoty funkce InputBox. A to z toho důvodu, že i když se uživatel již jednou zmýlil, není vůbec vyloučeno, že tak udělá i podruhé. 

    

  1. Kód, jenž následuje za návěstím Tvorba_Formulářů, inicializuje pole frm() na hodnotu o jednotku menší nežli je hodnota parametru Počet. Zde je důležité zdůraznit, že Visual Basic .NET přistupuje k deklaraci polí jinak než jeho předchůdce. Dolní hranice pole je po novém vždy rovna nule, zatímco horní hranice není prakticky omezená. Při deklaraci pole je potřebné zadat hodnotu, která reprezentuje horní hranici pole (naproti tomu, v některých jiných jazycích platformy .NET se při deklaraci pole udává hodnota, jež představuje počet prvků pole – typicky v Managed Extensions for C++). Tak je připraveno pole, jehož prvky budou schopny uložit odkazy na instance třídy Form. V cyklu jsou všechny prvky pole naplněny příslušnými odkazy na vytvořené instance třídy Form.      

 

K použití instance zapečetěné třídy nám bude stačit několik řádků programového kódu:

 

        Dim x As New ZapečetěnáTřída()

        x.VytvořitFormuláře(3)

        Me.Text = "Počet vytvořených formulářů: " & _

        CStr(x.ZjistitPočetFormulářů)

 

Zpět na obsah

 

Přístup k registrům operačního systému Windows

 

Snad všichni programátoři a vývojáři chtějí, aby byly jejich aplikace co možná nejvíc uživatelsky přívětivé. Uživatelská přívětivost je značně rozsáhlý pojem, jenž v sobě absorbuje velké množství doporučených standardů, které říkají, jak má aplikace vypadat a jak se má chovat. Dodržováním těchto standardů tak mohou programátoři zabezpečit, že uživatelům se bude s aplikací pracovat pohodlně, rychle a bez jakýchkoliv potíží. Jedním z požadavků, které jsou na reálnou aplikaci kladeny, je schopnost aplikace pamatovat si potřebné informace o stavech, v nichž se během své činnosti ocitla. Tak si aplikace může pamatovat seznam posledně otevřených souborů, přesnou pozici svého dialogového okna, nebo časový interval pro automatické uložení editovaného dokumentu. Na uložení a posléze načítaní všech uvedených informací lze s výhodou využít registrů operačního systému.

 

Ve Visual Basicu .NET můžeme aplikovat dvě koncepce přístupu k registrům:

 

  1. Starou koncepci, která je známá již z Visual Basicu 6 a při které se používají vestavěné funkce pro práci s registry (jde o funkce SaveSetting, GetSetting, DeleteSetting a GetAllSettings). Nevýhodou této koncepce je to, že všechny informace lze uložit jenom do předem určené sekce registrů (přesněji jde o sekci s názvem HKEY_CURRENT_USER\Software\VB and VBA Program Settings). Pro uložení informací do jiných sekcí registrů je nutné zavolat adekvátní API funkce, ovšem jedná se o značně náročnou záležitost. Použijete-li novou koncepci, nemusíte si s tímto omezením dále lámat hlavu, protože nové třídy rámce .NET Framework vám umožňují provádět zápis do libovolných oblastí registrů.   

 

  1. Novou a unikátní koncepci, kterou nabízí Visual Basic .NET. Pro přístup k registrům použijeme třídy za tímto účelem vytvořené (přesněji jde o třídy Registry a RegistryKey). Ačkoliv je použití nové koncepce poněkud komplikovanější, můžete údaje o své aplikaci ukládat do rozličných sekcí registrů, čímž je okamžitě likvidováno omezení staré koncepce.

 

Abyste viděli, jak obě koncepce pracují, ukážeme si jejich použití v praxi. Pokaždé provedeme vytvoření registrového klíče, jemuž přiřadíme příslušnou hodnotu a tuto hodnotu naplníme předem určenými daty.

 

Při práci s registry je zapotřebí odlišovat dva pojmy: jméno hodnoty a data hodnoty. Zatímco jméno hodnoty představuje textový řetězec, který hodnotu uživatelsky deklaruje, data hodnoty determinují hodnotu po datové stránce. Jednoduše řečeno, data hodnoty jsou vlastnictvím hodnoty, a jsou to právě data, které jsou předmětem vstupně-výstupních operací při práci s registry. Mezi data hodnoty mohou patřit čísla, textové řetězce, nebo specifické konstanty. 

 

Registry operačního systému jsou velmi důležitou entitou, a to jak ve vztahu k nainstalovaným aplikacím, tak i vůči samotnému operačnímu systému. Před zásahem do registrů proveďte vždy nejprve jejich zálohu. Při zásazích do registrů se ujistěte, že nemodifikujete údaje, které byly zapsány jinou aplikací a přímo upravujte jenom ty informace, které jste do registrů sami zapsali. 

 

Demonstrace staré koncepce přístupu k registrům

Postupujte podle následujících instrukcí:

 

  1. Spusťte Visual Basic .NET a vytvořte novou aplikaci pro Windows (Windows Application).
  2. Na formulář přidejte dvě instance ovládacího prvku Button, přičemž jednu pojmenujte jako btnZápisHodnot a druhou zase btnČteníHodnot. Rovněž vhodně upravte vlastnost Text obou instancí.  
  3. Událostní proceduru Click instance btnZápisHodnot upravte takto:

 

    Private Sub btnZápisHodnot_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnZápisHodnot.Click

        SaveSetting("MojeAplikace", "Data", "ID aplikace", "1001")

    End Sub

 

  1. Událostní proceduru Click instance btnČteníHodnot modifikujte tímto způsobem:

 

    Private Sub btnČteníHodnot_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnČteníHodnot.Click

        MessageBox.Show("ID aplikace je: " & _

        GetSetting("MojeAplikace", "Data", "ID aplikace"))

    End Sub

 

  1. Spusťte aplikaci (F5) a klepněte nejprve na tlačítko pro zápis hodnot do registrů a poté aktivujte druhé tlačítko. Měli byste obdržet informace o datech hodnoty klíče \MojeAplikace\Data\ID aplikace. Podíváte-li se do registrů, uvidíte, že byl vytvořen příslušný klíč a byla mu přiřazena hodnota (obr. 1).

 

 

Obr. 1 – Použití funkcí SaveSetting a GetSetting pro přístup k hodnotě uložené v registrech

 

Demonstrace nové koncepce přístupu k registrům

Jak jsme si již řekli, při použití této koncepce se vyhneme použití funkcí typu SaveSetting a GetSetting. Místo nich totiž povoláme k práci specializované třídy Registry a RegistryKey z jmenného prostoru Microsoft.Win32. Postupujte takto:

 

  1. Rozšiřte stávající kolekci instancí ovládacího prvku Button o další dvě, které pojmenujte jako btnZápisHodnot2 a btnČteníHodnot2 (opět upravte také hodnoty vlastnosti Text obou instancí).
  2. Před kód třídy formuláře (implicitně Form1) vložte následující řádek pro vložení jmenného prostoru Microsoft.Win32:

 

Imports Microsoft.Win32

 

  1. Událostní proceduru Click instance btnZápisHodnot2 pozměňte následovně:

 

        

    Private Sub btnZápisHodnot2_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnZápisHodnot2.Click

        Dim reg As RegistryKey

        reg = Registry.LocalMachine.CreateSubKey("Software\MojeAplikace\Data")

        reg.SetValue("ID aplikace", "1002")

    End Sub

 

Pohled do registrů přibližuje obr. 2.

 

 

Obr. 2 – Použití tříd Registry a RegistryKey pro přístup k hodnotě uložené v registrech

 

I když opět vytváříme klíč se stejným názvem, v tomto případě je klíč uložen do jiné sekce registrů (přesněji do HKEY_LOCAL_MACHINE\Software). Abychom získali přístup do této sekce registrů, musíme použít třídu Registry a její veřejný sdílený datový člen LocalMachine. Pro vytvoření nového klíče použijeme metodu CreateSubKey, které předáme doplňkovou cestu ke klíči v uvedené podobě (plná cesta k našemu klíči vypadá takto: HKEY_LOCAL_MACHINE\Software\MojeAplikace\Data). Návratovou hodnotou metody CreateSubKey je instance třídy RegistryKey, přičemž odkaz na tuto instanci je uložen do proměnné reg. Dobrá, v této chvíli máme vytvořen klíč, ovšem náš úkol ještě není splněn. Abychom klíči přiřadili hodnotu a hodnotu asociovali s platnými daty, zavoláme metodu SetValue, která nabídneme jméno hodnoty ve tvaru datového typu String ("ID aplikace") a data hodnoty (1002).

 

Budete-li chtít data hodnoty načíst, upravte událostní proceduru btnČteníHodnot2_Click tak, jak je uvedeno níže:

 

    Private Sub btnČteníHodnot2_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnČteníHodnot2.Click

        Dim reg2 As RegistryKey

        reg2 = Registry.LocalMachine.OpenSubKey("Software\MojeAplikace\Data", False)

        MessageBox.Show("Hodnota zapsaná v registrech: " & _

        CStr(reg2.GetValue("ID aplikace")))

    End Sub

 

Abychom otevřeli požadovaný registrový klíč, zavoláme metodu OpenSubKey. V našem případě je použita přetížená varianta metody, která pracuje se dvěma parametry: prvním je název klíče a druhým pak booleovská hodnota, která určuje, zdali je možné hodnotu klíče modifikovat. K datům hodnoty se dostaneme pomocí metody GetValue, která poskytneme jméno hodnoty, data které chceme obdržet. Metoda GetValue vrací data hodnoty (v podobě datového typu Object), která jsou v naší ukázce přetypována do podoby datového typu String a zobrazena v dialogu.

 

Zpět na obsah

 

Vytváření vlastního delegáta

 

Přecházíte-li na programování pro platformu .NET, zcela jistě si zanedlouho všimnete speciálního programovacího aparátu, který se doposud v programování nevyskytoval. Ano, touto novinkou jsou delegáti, neboli objekty, které usnadňují přístup k metodám a funkcím jiných objektů za běhu programu. O delegátech se často mluví jako o typově bezpečných funkčních ukazatelích. Pokud patříte mezi programátory v C/C++, mohli byste namítat, že funkční ukazatele jsou tady již nějaký ten pátek, tak jakážto novinka. Ovšem, v jistém smyslu máte pravdu, avšak třeba zdůraznit, že delegáty nabízejí daleko větší funkcionalitu nežli opravdové funkční ukazatele. Kromě toho, s delegáty můžete pracovat v mnoha .NET jazycích, např. v C# či Managed Extensions for C++.

 

V dnešní ukázce si předvedeme, jak sestrojit vlastního delegáta. Ještě předtím se však podívejme na základní způsob práce delegáta. Delegát je objekt, jenž může za běhu aplikace získat runtime adresu požadované procedury a posléze tuto proceduru aktivovat. Ve skutečnosti ovšem může delegát aktivovat jakoukoliv proceduru, která má stejný seznam parametrů a stejnou návratovou hodnotu jako samotný delegát. Delegát tak nemusí nutně volat jednu a tu samou proceduru, neboť je schopen aktivovat libovolnou proceduru, která vyhovuje požadovaným podmínkám. Rozhodnutí, kterou proceduru zavolat, lze vyřešit až za běhu aplikace.   

 

 

Obr. 3 – Schematické znázornění práce delegáta

 

A nyní si ukažme, jak přistoupit k tvorbě delegáta:

 

  1. Spusťte Visual Basic .NET a vytvořte aplikaci pro Windows (Windows Application).
  2. Na plochu formuláře klepněte pravým tlačítkem myši a z kontextové nabídky vyberte položku View Code.
  3. Programový kód třídy Form1 upravte podle níže uvedeného vzoru:

 

Option Strict On

'Vložené odkazy na potřebné jmenné prostory.

Imports System.Drawing

Imports System.Drawing.Drawing2D

 

Public Class Form1

    Inherits System.Windows.Forms.Form

 

    'Deklarace delegáta.

    Private Delegate Function Delegát() As Boolean

 

    '****** Deklarace první cílové funkce. ******

    'Funkce vytváří instanci třídy ListBox, do které budou uložena

    'náhodně vygenerovaná čísla.

    Private Function MojeFunkce() As Boolean

        Try

            'Vytvoření instance třídy ListBox a její umístění

            'do kolekce ovládacích prvků formuláře.

            Dim lstSeznam As New ListBox()

            With lstSeznam

                .Location = New Point(0, 0)

                .Size = New Size(Me.Width \ 2, Me.Height \ 2)

            End With

            Me.Controls.Add(lstSeznam)

 

            'Výpočet druhé mocniny čísel z intervalu <0, 9>.

            Dim a As Byte

            a = 0

            Do While a < 10

                Dim x As Short

                x = CShort(a ^ 2)

                lstSeznam.Items.Add(x)

                a += CByte(1)

            Loop

            'Zachycení jakékoliv výjimky, která by mohla být generována.

        Catch e As Exception

            'Dojde-li k chybě, funkce je ukončena a vrací hodnotu False.

            Return False

        End Try

        'Při úspěšném ukončení funkce je vrácena hodnota True.

        Return True

    End Function

 

    '****** Deklarace druhé cílové funkce. ******

    'Funkce vyplňuje plochu formuláře gradientní výplní.

    Private Function MojeKreslícíFunkce() As Boolean

        Try

            'Vytvoření instance třídy Rectangle

            Dim rec As New Rectangle(New Point(0, 0), _

                    New Size(Me.Width, Me.Height))

            'Vytvoření grafického štětce pomocí třídy LinearGradientBrush

            Dim br As New LinearGradientBrush(rec, Color.Gold, _

            Color.Magenta, LinearGradientMode.ForwardDiagonal)

            'Vytvoření grafického objektu

            Dim g As Graphics = Me.CreateGraphics()

            'Kreslení gradientní výplně.

            g.FillRectangle(br, rec)

        Catch e As Exception

            'Dojde-li k chybě, funkce je ukončena a vrací hodnotu False.

            Return False

        End Try

        'Při úspěšném ukončení funkce je vrácena hodnota True.

        Return True

    End Function

 

    Private Sub Form1_Click(ByVal sender As Object, _

    ByVal e As System.EventArgs) Handles MyBase.Click

        'Inicializace generátoru náhodných čísel na bázi systémového času

        Randomize()

        'Deklarace proměnné, ve které bude uloženo získané

        'náhodné číslo

        Dim sHodnota As Short

        'Vytvoření referenční proměnné, která bude uchovávat odkaz

        'na později vytvořenou instanci delegáta

        Dim InstanceDelegáta As Delegát

        'Generování náhodného čísla z intervalu <1,10>

        sHodnota = CShort(Int(10 * Rnd()) + 1)

        'Jestliže je vygenerované číslo z intervalu <1,5> tak...

        If sHodnota >= 1 And sHodnota <= 5 Then

            'Vytvoření instance delegáta, která obsahuje

            'runtime adresu funkce MojeFunkce

            InstanceDelegáta = New Delegát(AddressOf MojeFunkce)

            'jinak...

        Else

            'Vytvoření instance delegáta, která obsahuje runtime

            'adresu funkce MojeKreslícíFunkce

            InstanceDelegáta = New Delegát(AddressOf MojeKreslícíFunkce)

        End If

        'Test návratové hodnoty cílové funkce

        If InstanceDelegáta.Invoke() Then

            Me.Text = "Funkce byla úspěšně zavolána."

        Else

            Me.Text = "Při práci funkce došlo k potížím."

        End If

    End Sub

End Class

 

Zpět na obsah

 

 

Právě jste dočetli poslední tip sekce Programátorská laboratoř. 

 

Jestliže se chcete dozvědět více informací o programování ve Visual Basicu .NET, neváhejte a navštivte také další sekce rubriky Visual Basic. A jakáže je dnešní nabídka?

Téma měsíce

Programování vícevláknových aplikací

Seriál Začínáme s VB .NET

Klasifikace operátorů, aritmetické a porovnávací operátory.

 

Nepřehlédněte exkluzivní Speciál pro programátory, který pojednává o jazykové interoperabilitě mezi jazyky Managed Extensions for C++ a Visual Basic .NET.