Prawo Maleka: najprostsze myśli formułowane są w najbardziej skomplikowany sposób.
Visual Basic 6 - Zmienne i stałe

Zmienne

Jak w każdym języku programowania, tak i w Visual Basic używa się zmiennych do czasowego przechowywania różnych wartości podczas pracy programu. Każda zmienna posiada nazwę - słowo poprzez które odwołujemy się do jej zawartości - i określony typ, który determinuje, jaki rodzaj danych może być w zmiennej przechowywany.

Możesz traktować zmienną jako miejsce w pamięci komputera do przechowywania nieznanych w momencie tworzenia programu wartości. Pozwala to na wykonywanie dowolnych obliczeń na wartościach, które pojawią się dopiero po uruchomieniu programu i mogą być za każdym razem inne.

Zapisywanie i odczytywanie wartości zmiennych

Do zmiennej można przypisać wartość, aby wykonać na niej jakieś obliczenia i wynik przypisać również do zmiennej:

     x = 10        ' do zmiennej x przypisano wartość 10
     x = x + 1     ' wartość zmiennej x została powiększona o 1 
Zauważ, że znak równości jest operatorem przypisania.

Deklarowanie zmiennych

Deklaracja zmiennej jest poinformowaniem programu o jej istnieniu. Zmienne deklarujemy poleceniem Dim podając co najmniej nazwę deklarowanej zmiennej:

     Dim NazwaZmiennej [As typ]

Zmienne zadeklarowane wewnątrz procedury lub funkcji istnieją tylko tak długo, jak długo wykonywana jest procedura lub funkcja. Po ich zakończeniu zmienne są usuwane z pamięci. Dlatego też takie zmienne nazywamy lokalnymi: zmienne i ich wartości nie są dostępne w żadnej innej procedurze lub funkcji. Dzięki temu można w różnych procedurach używać zmiennych o identycznych nazwach bez obawy, ze zmiana wartości zmiennej w jednej procedurze będzie miała wpływ na zmienną o identycznej nazwie w innej procedurze.

Nazwa zmiennej:

  • musi zaczynać się literą;
     
  • może składać się z liter (również polskich), cyfr i znaków podkreślenia;
     
  • może mieć do 255 znaków długości;
     
  • musi być unikalna w zasięgu swojej widoczności (np. nie wolno zadeklarować 2 zmiennych x w jednej procedurze);
     
  • nie może być słowem kluczowym języka Visual Basic.
     
Opcjonalna klauzula As pozwala zdefiniować typ deklarowanej zmiennej określający, jakie dane mogą być w zmiennej przechowywane, np.: String, Integer lub Currency. Zmienne mogą przechowywać również obiekty i klasy.

Istnieją również inne metody deklarowania zmiennych:

  • w sekcji deklaracji Formy, modułu lub klasy: tak zadeklarowane zmienne widoczne są we wszystkich procedurach i funkcjach Formy, modułu czy klasy;
     
  • zadeklarowanie zmiennej klauzulą Public udostępnia ją wszystkim modułom aplikacji;
     
  • zadeklarowanie zmiennej klauzulą Static zachowuje jej wartość przez cały czas pracy aplikacji: nawet po zakończeniu procedury lub modułu.
     

Domyślna (Implicit) deklaracja

Visual Basic pozwals na używanie zmiennych bez ich wcześniejszego deklarowania. Np.:

     Function Pierwiastek(x)
        Temp        = Abs(x)
        Pierwiastek = Sqr(Temp)
     End Function
Visual Basic automatycznie tworzy zmienną o nazwie, której użyjesz. Ponieważ jednak nie jest znany typ takiej zmiennej, to automatycznie przypisywany jest jej typ Variant, który wymaga dużo pamięci i znacznie zwalnia pracę aplikacji. Inną nieprzyjemną konsekwencją takiego używania zmiennych jest możliwość pomyłki literowej przy pisaniu programu. Zobacz kolejny przykład:
     Function Pierwiastek(x)
        Temp        = Abs(x)
        Pierwiastek = Sqr(Tmp)
     End Function

Na pierwszy rzut oka niczym nie różni się od poprzedniego. Jednak użyte tu zmienne Temp i Tmp. Kompilator nie domyśli się, że chodzi nam o tą samą zmienna i wyniku ta funkcja zawsze zwróci 0, gdyż kompilator utworzy dwie różne zmienne.

Jawna (Explicit) deklaracja

Aby zapobiec tego typu "wypadkom" można wymusić sprawdzanie przez kompilator, czy każda użyta zmienna została wcześniej zadeklarowana. Należy w tym celu każdy moduł kodu aplikacji rozpocząć od polecenia:

     Option Explicit
Nasza przykładowa funkcja wyglądałaby wtedy tak:
     Function Pierwiastek(x)
        Dim Temp as Double
        Temp        = Abs(x)
        Pierwiastek = Sqr(Tmp)
     End Function
Przy takim zapisie i użyciu polecenia Option Explicit brak deklaracji zmiennej Tmp spowoduje wyświetlenie komunikatu o nie zadeklarowanej zmiennej. Polecenie Option Explicit musi być użyte w każdym module, w którym chcemy mieć kontrolę deklaracji zmiennych. Musi ono być użyte na początku modułu, jeszcze przed pierwszą procedurą, funkcją lub deklaracją zmiennych.

Typy danych

Typ zmiennej determinuje jakiego typu dane mogą być w niej przechowywane. Domyślnym - nie wymagającym deklaracji typem danych jest Variant. Jest to typ-kameleon: może przechowywać dane każdego typu. Nie wymaga również konwersji podczas przypisywania: Visual Basic automatycznie wykonuje konieczną konwersję. Odbywa się to jednak kosztem ilości pamięci i czasu wykonania.

Znając rodzaj danych przechowywanych w zmiennej bardziej optymalnym rozwiązaniem jest zadeklarowanie odpowiedniego jej typu.

Można również deklarować tablice o elementach dowolnego typu podstawowego.

Deklarowanie zmiennych ze zdefiniowanym typem

Zmienne deklarujemy instrukcją: Dim, Private, Public lub Static podając po niej nazwę zmiennej słowo kluczowe As i po nim typ zmiennej. Możemy w jednej instrukcji deklarować wiele zmiennych, ale każda musi mieć indywidualnie zdefiniowany typ. Np.:
     Private i As Integer           ' zmienna i typu całkowitego
     Dim d As Double                ' zmienna d typu rzeczywistego
     Static Imię As String          ' zmienna Imię typu łańcuchowego
     Public Cena As Currency        ' zmienna Cena typu walutowego

     Private a As Integer, s As Single        ' zmienna a - całkowita i s - rzeczywista
     Private data As Date, Wart As Currency   ' zmienna data - data i Wart - walutowa
     Private x, y As Integer                  ' zmienna x - Variant i y - całkowita
Zwróć uwagę na ostatni przykład: zmienna x bez zdefiniowanego typu ma automatycznie przypisany typ Variant.Jest to istotna różnica w stosunku do większości innych języków programowania.

Deklarowanie zmiennych łańcuchowych

Zmienne typu String służące do przechowywania łańcuchów znakowych możemy deklarować na 2 sposoby.

Domyślnie - podając tylko typ String - tworzymy zmienną, w której możemy przechowywać praktycznie łańcuch znaków nieograniczonej długości.

Możemy jednak zadeklarować łańcuch znaków o stałej długości:

     String * długość
Np.:
     Dim Nazwisko As String * 25
W tym przypadku krótsze łańcuchy znaków będą uzupełnione spacjami do podanej długości, a dłuższe zostaną obcięte z prawej strony za 25 znakiem.

Łańcuchy stałej długości można deklarować w modułach jako Public lub Private. W Formach i klasach muszą być zadeklarowane jako Private.

Wymiana łańcuchów i numeryków

Do zmiennej typu String można zawsze przypisać wartość zmiennej typu numerycznego. Visual Basic automatycznie dokona poprawnej konwersji typu.

Możliwe jest również odwrotne przypisanie, ale tylko wtedy, gdy zmienna typu String rzeczywiście zawiera poprawną wartość numeryczną. W innym wypadku powstanie błąd wykonania (run-time error).

Np.:
     Dim intX As Integer
     Dim strY As String
     strY = "100.23"
     intX = strY               ' przypisanie łańcucha do zmiennej numerycznej 
     List1.AddItem Cos(strY)   ' dodanie do ListBox'a cosinusa obliczonego z napisu
     strY = Cos(strY)          ' przypisanie do łańcucha cosinusa obliczonego z napisu 

Wartość Empty

Wartość Empty mogą posiadać tylko zmienne typu Variant. Nie posiada jej żaden inny typ danych. Wartość Empty jest czymś innym niż 0 lub pusty łańcuch znaków (""): oznacza, że zmienna od momentu zadeklarowania nie miała przypisanej jakiejkolwiek wartości. Może ona służyć do sprawdzenia, czy zmienna została zainicjowana.

Użycie pustej (Empty) zmiennej typu Variant w wyrażeniu spowoduje, że będzie ona traktowana jako 0 lub pusty łańcuch znaków ("") zależnie od typu wyrażenia.

Zmienna typu Variant ma wartość Empty, tak długo, aż nie przypiszemy do niej innej wartości (włączając w to 0, "" i Null). Można do zmiennej ponownie przypisać wartość Empty.

Wartość Null

Zmienna typu Variant może przyjmować specjalna wartość: Null. Null jest wartością używana w bazach danych dla oznaczenia nieznanej lub brakującej wartości pola. Wartość Null posiada kilka unikalnych cech:
  • Wyrażenie, w którym chociaż jedna wartość przyjmuje Null zawsze zwraca w wyniku Null.
     
  • Większość funkcji zwraca Null, jeżeli któryś z parametrów ma wartość Null.
     
  • Wartość Null może być przypisana tylko do zmiennej typu Variant. Próba przypisania do jakiegokolwiek innego typu spowoduje błąd.
     
  • Funkcja może zwracać wartość Null tylko wtedy, gdy jest typu Variant.
     

Podstawowe typy danych

Poniższa tabela przedstawia podstawowe typy danych, ich opisy i zakresy danych, które mogą przechowywać, wielkości pamięci jakiej używają i konwencjonalne przedrostki deklaracji typu.
 
Nazwa typu Typ danych Pamięć
(w bajtach)
Zakres wartości
Booleanlogiczna1True lub False
Byteliczba całkowita10 do 255
Integerliczba całkowita2-32,768 do 32,767
Longliczba całkowita
długa
4-2,147,483,648 do 2,147,483,647
Singleliczba rzeczywista4Ujemne:-3.402823 E 38 do -1.401298 E -45
Dodatnie: 1.401298 E -45 do 3.402823 E 38
Doubleliczba rzeczywista8Ujemne:-0.79769313486232 E 308 do -4.94065645841247 E -324
Dodatnie: 4.94065645841247 E -324 do 1.79769313486232 E 308
Currencyliczba rzeczywista
z symbolem waluty
8-922 337 203 685 477.5808 do -922 337 203 685 477.5807
Datedata (mm/dd/rrrr)81/1/100 do 12/31/9999
Stringłańcuch znakówdługość łańcuchazmiennej długości: do 231 znaków
stałej długości: do 64 KB (216) znaków
Objectadres obiektu4referencja do obiektu
Variantdowolne dane>=16jak dla typu przechowywanych danych

Uwagi:
  • Tablice dowolnego typu wymagają 20 bajtów pamięci plus 4 bajtów dla każdego wymiaru plus pamięć dla przechowywanych danych. Np. tablica o wymiarach 10 wierszy po 10 kolumn przechowująca liczby całkowite typu Integer wymaga:
    20 bajtów + 2 x 4 bajty + 10 x 10 x 2 bajty = 228 bajtów.
     
  • Zmienna typu Variant przechowująca tablicę wymaga 12 bajtów więcej niż sama tablica.
     
  • Używanie zmiennych typu Variant - mimo ich dużej wygody - nie jest zalecane: wymagają one więcej pamięci i praca z nimi znacznie spowalnia program. Dlatego poza wyjątkowo uzasadnionymi przypadkami nie należy ich używać.
     
  • Istnieje jeszcze 12 typ danych: typ danych zdefiniowanych przez użytkownika. Jest to hybryda, która umożliwia łączenia różnych elementów podstawowych typów danych w bardziej złożone struktury.
     

Tworzenie własnych typów danych

Deklarowanie typu użytkownika

Można stworzyć kombinację zmiennych różnych typów jako typ danych użytkownika (ang. user-defined type). Stanowi to odpowiednik typu rekordowego z Pascala lub struktury z C/C++. Może to być wygodne do przechowywania wielu powiązanych z sobą informacji w jednej zmiennej.

Własne typy danych deklarujemy instrukcją Type, która musi być umieszczona w części deklaracyjnej modułu (przed wszystkimi procedurami i funkcjami). Własne typy możemy deklarować jako prywatne lub publiczne za pomocą odpowiednio klauzul: Private i Public. Np.:

     Private Type MójTyp
lub
     Public Type MójTyp
Deklaracja typu kończy się klauzulą End Type. Tak mogłaby wyglądać deklaracja typu zawierającego podstawowe dane osobowe w jakiejś kartotece:
     Private Type Osoba
        Nazwisko As String * 25
        Imię As Srting * 15
        DataUrodzenia As Date
     End Type

Deklarowanie zmiennej własnego typu

Po stworzeniu własnego typu możemy go wykorzystać do tworzenia zmiennych. Należy pamiętać, że sama deklaracja typu nie tworzy jeszcze żadnej zmiennej. Zmienne własnych typów - jak wszystkie inne - możemy deklarować jako lokalne, prywatne wewnątrz modułu lub publiczne. Możemy również tworzyć tablice własnego typu. Np.:
     Dim Ja As Osoba, Ty As Osoba, My (2) As Osoba
Poniższa tabela pokazuje jakiego rodzaju typy i ich zmienne możemy tworzyć zależnie od miejsca, gdzie definiujemy własny typ danych:
Miejsce deklaracji Typ może być: Zmienna może być:
Procedures niedozwolone tylko Private
moduł kodu Private lub Public Private lub Public
moduł formularza tylko Private tylko Private
Class modules Private lub Public Private lub Public
Uwaga: własny typ zadeklarowany poleceniem Dim w module kodu lub klasy jest domyślnie traktowany jako Public. Jeżeli chcemy zadeklarować go jako prywatny, to musimy zaznaczyć to jednoznacznie używając w deklaracji słowa kluczowego Private.

Praca ze zmiennymi własnego typu

Korzystnie ze zmiennych zdefiniowanych przez siebie typów jest podobna do korzystania z właściwości obiektów: nazwę zmiennej od jej poszczególnych elementów oddzielamy kropką. Np:
     Ja.Imię = "Władysław"
     If Ty.DataUrodzenia > #1/1/2000# Then
Jeżeli mamy kilka zmiennych własnego typu, to wymiana danych między nimi może odbywać się przez przypisanie kolejnych elementów jednej zmiennej do drugiej lub przez przypisanie całej zmiennej. Np:
     Ja.Imię = Ty.Imię
     Ja.Nazwisko = Ty.Nazwisko
     Ja.DataUrodzenia = Ty.DataUrodzenia

lub krócej

     Ja = Ty

Typ użytkownika zawierający tablice

Definiowany przez użytkownika typ danych może zawierać również tablice. Np:
     Type SystemInfo
        CPU As Variant
        Memory As Long
        DiskDrives (25) As String   ' tablica o 25 elementach typu String
        VideoColors As Integer
     End Type
Tablice w definiowanym przez użytkownika typie danych mogą też być dynamiczne. Np:
     Type SystemInfo
        CPU As Variant
        Memory As Long
        DiskDrives () As String      ' tablica dynamiczna
        VideoColors As Integer
     End Type
Dostęp do tablic w zmiennych typu użytkownika jest podobna do korzystania z właściwości obiektów:
     Dim MySystem As SystemInfo
     ReDim MySystem.DiskDrives(3)
     MySystem.DiskDrives(0) = "1.44 MB"
Jak już wspomnieliśmy, można również tworzyć tablice własnego typu. Sposób odwoływania się do jej poszczególnych elementów jest analogiczny:
     Dim Systems(100) As SystemInfo

     Systems(5).CPU = "386SX"
     Systems(5).DiskDrives(2) = "100M SCSI"

Zmienne typu użytkownika jako parametry

Zmienne typów zdefiniowanych przez użytkownika mogą być przekazywane jako parametry do innych procedur lub funkcji na takich samych zasadach, jak wszystkie inne zmienne. Np.:
     Sub SystemData (SystemX As SystemInfo)
        SystemX.CPU = lstCPU.Text
        SystemX.Memory = txtMemory.Text
        SystemX.DiskDrives = cmbDrive.Text
        SystemX.VideoColors = cmbVideo.Listindex
     End Sub
Uwaga: przekazanie zmiennej typu użytkownika wewnątrz modułu formularza wymaga, aby procedura była zadeklarowana jako prywatna.

Zmienne typów zdefiniowanych przez użytkownika mogą być przekazywane jako parametry do procedur i zwracane przez funkcje. Zmienne tego typu zawsze przekazywane są przez referencję. Oznacza to, że zmiana wartości parametru wewnątrz procedury lub funkcji zostaje w niej zapamiętana. Widać to w powyższym przykładzie.

Ponieważ zmienne typu użytkownika zawsze są przekazywane przez referencję, to wymaga to każdorazowego przekazania wszystkich danych do i z procedury/funkcji. Jeżeli zmienna zawiera duże tablice może być to dość czasochłonne (zwłaszcza przy aplikacjach sieciowych). Często lepszym rozwiązaniem jest wydzielenie z typu tylko niezbędnych danych i przekazanie ich do wywoływanej procedury: może to znacznie przyspieszyć pracę programu.

Zagnieżdżanie struktur danych

Zagnieżdżanie może być dowolnie skomplikowane. Wynika z tego, ze typ zdefiniowany przez użytkownika może zawierać inny typ użytkownika, itd. Widać to w poniższym przykładzie:
     Type Dysk
        Typ As String
        Wielkość As Long
     End Type

     Type SystemInfo
        CPU As Variant
        Memory As Long
        Dyski(26) As Dysk
     End Type

     Dim Systems(100) As SystemInfo
     ...
     Systems(1).Dyski(0).Typ = "FDD"
Ze względów praktycznych - żeby łatwiej było analizować program i śledzić złożoność struktur - dobrą metoda jest umieszczenie wszystkich definicji własnych typów w kodzie jednego modułu. Może to znacznie ułatwić wykrywanie ewentualnych błędów oraz konieczność wprowadzenia zmian do aplikacji.

Zasięg i czas życia zmiennych

Miejsce oraz sposób deklaracji zmiennej decydują o tym, która część kody będzie tą zmienna "widziała" (mogła się do niej odwoływać), a która nie.

Deklarując zmienną wewnątrz procedury powodujemy, że będzie widziana tylko w tej procedurze: zostanie utworzona i zainicjowana domyślną wartością w momencie wywołania procedury, a po zakończeniu działania procedury zostanie usunięta z pamięci. Nie będzie również widziana przez inne procedury, które z niej zostaną wywołane (chyba, że przekażemy ją do nich jako parametr). Takie zmienne nazywamy lokalnymi zmiennymi procedury.

Zmienne zdefiniowane w części deklaracyjnej modułu (przed wszystkimi procedurami i funkcjami) będą widziane przez wszystkie procedury tego modułu. Są to zmienne lokalne modułu.

Zmienne możemy deklarować za pomocą czterech słów kluczowych: Public, Private, Dim i Static. Decydują one o tym, gdzie zmienna będzie widziana i jak długo będzie istniała w pamięci:

  • Public - może być użyte tylko w części deklaracyjnej modułu. Powoduje, że zmienna istnieje przez cały czas pracy aplikacji i jest widziana we wszystkich procedurach wszystkich modułów.
     
  • Private - może być użyta wszędzie. Powoduje, że zmienna jest tworzona przy każdym uruchomieniu modułu lub procedury, w której została zadeklarowana i usuwana z pamięci po jej zakończeniu. Widoczna jest wewnątrz procedury, w której została zadeklarowana lub we wszystkich procedurach modułu, jeżeli zadeklarowaliśmy ją w części deklaracyjnej modułu.
     
  • Dim - jest to synonim klauzuli Private.
     
  • Static - może być użyte tylko wewnątrz procedury. Zasięg jej widoczności jest taki, jak w przypadku Private lub Dim. Różnica polega na tym, że zmienna nie jest usuwana z pamięci po zakończeniu procedury, ale zachowuje swoją wartość. Umożliwia to przechowanie wartości lokalnych zmiennych procedury między jej kolejnymi wywołaniami.
     

Poniższa tabelka pokazuje zasięg widoczności i czas życia zmiennych w zależności od tego gdzie i jakim słowem kluczowym zostaną zadeklarowane:

Miejsce Słowo kluczowe Zasięg Czas życia
Moduł Public wszystkie moduły,
wszystkie procedury
cały czas pracy aplikacji
Moduł Private/Dim wszystkie procedury
modułu
  • moduł kodu: cały czas pracy programu
     
  • moduł formularza: od załadowania formularza do pamięci do jego usunięcia
     
  • moduł klasy: od utworzenia instancji klasy do jej usunięcia
     
 
Procedura Private/Dim tylko wewnątrz
procedury
od uruchomienia procedury do jej zakończenia
 
Procedura Static tylko wewnątrz
procedury
od pierwszego wywołania procedury do końca pracy programu

Przesłanianie zmiennych

Zmienne i procedury publiczne

Z przesłanianiem zmiennych mamy do czynienia, gdy ta sama nazwa zmiennej zostanie użyta w różnych częściach programu i jednocześnie są (mogą być) widoczne wewnątrz jednej procedury. Ma to miejsce, gdy zadeklarujemy w różnych modułach zmienne publiczne o tej samej nazwie (choć niekoniecznie o tych samych typach) lub gdy wewnątrz procedury mamy zmienną lokalna o nazwie identycznej ze zmienna publiczną.

Generalną regułą w Visual Basic'u jest wyższy priorytet zmiennych lokalnych.

W pierwszym przypadku problem możemy rozwiązać podając nazwę kwalifikowana zmiennej, czyli poprzedzając ją nazwą modułu, z którego zmienną chcemy pobrać.

Rozpatrzmy to przykładzie. Utwórz projekt składający się z dwóch modułów i jednego formularza. Na formularzu umieść trzy przyciski poleceń (CommandButton). Następnie zapiszemy poniższy kod:

	'W module Module1:
		Public x As Integer      ' deklaracja zmiennej publicznej
	
		Sub Test()
		   x = 123   
		End Sub
	
	'W module Module2:
		Public x As String       ' deklaracja zmiennej publicznej
	
		Sub Test()
		   x = "ABCDEFGH..."
		End Sub
	
	'W module Form1:
		Public x As Date         ' deklaracja zmiennej publicznej
	
		Sub Test()
		   x = Date()
		End Sub
		
		' oraz procedury obsługi kliknięcia na każdy z przycisków
		Private Sub Command1_Click()
		   Module1.Test               ' wywołanie procedury Test z Module1
		   MsgBox Module1.x           ' wyświetlenie zmiennej x z Module1
		End Sub
	
		Private Sub Command1_Click()
		   Module2.Test               ' wywołanie procedury Test z Module1
		   MsgBox Module2.x           ' wyświetlenie zmiennej x z Module1
		End Sub
	
		Private Sub Command1_Click()
		   Test                       ' wywołanie procedury Test z Module1
		   MsgBox x                   ' wyświetlenie zmiennej x z Module1
		End Sub
Po uruchomieniu tego programu kliknięcie na każdy z przycisków wyświetli odpowiedni komunikat: zmienną z wybranego modułu. Zwróć uwagę, że odwołanie do procedury Test() i zmiennej x z Form1 nie wymaga podania kwalifikatora. Dzieje się tak dlatego, że domyślnie wewnątrz każdego modułu widziane są jego własne, wewnętrzne zmienne. Ponieważ jest to działanie domyślne, to nie wymaga podawania żadnych kwalifikatorów.

Jak widać z powyższego przykładu te same reguły dotyczą powtarzających się nazw nie tylko zmiennych, ale również procedur i funkcji.

Zmienne publiczne i prywatne

Pozostaje jeszcze problem przesłaniania zmiennych publicznych przez lokalne zmienne procedury. Ma to miejsce wtedy, gdy w procedurze zadeklarujemy taką samą nazwę zmiennej, jaka istnieje jako zmienna publiczna lub prywatna w całym module. W Visual Basic'u pierwszeństwo zawsze mają zmienne lokalne. Dlatego w tej sytuacji znowu musimy odwołać się do nazw kwalifikowanych, aby pobrać wartość zmiennej publicznej.

Zobaczmy to na kolejnym przykładzie. Tym razem wystarczy nam sam formularz z dwoma przyciskami.

	Public x As Integer
	
	Private Sub Command1_Click()
	   Dim x As Integer
	   x = 2
	   MsgBox Form1.x      ' wyświetlenie zmiennej modułowej
	End Sub
	
	Private Sub Command2_Click()
	   Dim x As Integer
	   x = 2
	   MsgBox x            ' wyświetlenie zmiennej lokalnej
	End Sub
Należy przy tym pamiętać, aby zmienna modułowa była zmienną publiczna. Zadeklarowanie jej klauzulą Dim lub Private spowoduje błąd wykonania przy próbie odwołania się do nazwy kwalifikowanej Form1.x.

Ten sam problem dotyczy używania nazw lokalnych o nazwach identycznych z nazwami kontrolek lub ich właściwości: nazwa lokalana zawsze będzie miała wyższy priorytet! Zobaczmy to na kolejnym przykładzie. Tym razem na formularzu umieścimy tylko pole tekstowe.

	Private Sub Form_Load()
	   Text1 = "ABCD..."         ' przypisanie tekstu do kontrolki
	   Text1.Top = 1000          ' przemieszczenie kontrolki na formularzu
	   BackColor = vbWhite       ' ustawienie białego tła formularza
	End Sub
	
	Private Sub Form_Click()
	Dim Text1, BackColor
	   Text1 = "abcd..."         ' przypisanie tekstu do zmiennej lokalnej
	   Me.Text1 = "Kontrolka"    ' przypisanie tekstu do kontrolki
	   Text1.Top = 0             ' Tutaj wystąpi błąd wykonania
	   Me.Text1.Top = 0          ' przemieszczenie kontrolki na formularzu
	   BackColor = 0             ' przypisanie 0 do zmiennej lokalnej
	   Me.BackColor = 0          ' ustawienie czarnego tła formularza
	End Sub

Nazwy zmiennych i procedur

Wewnątrz modułu nie wolno używać tych samych nazw dla zmiennych i procedur. Spowoduje to powstanie błędu już na etapie kompilacji modułu. Może się zdarzyć, że nazwa zmiennej publicznej z jednego modułu będzie identyczna z nazwą publicznej procedury innego modułu. W takim przypadku zawsze musimy używać nazw kwalifikowanych, podajacych moduł, do którego zmiennej lub procedury się odwołujemy.

Ponieważ przesłanianie nazw może prowadzić nieraz do nieprzewidzianych zachowań programu, dlatego dobrym zwyczajem jest unikanie powtarzających się nazw i nadawanie każdej zmiennej i procedurze publicznej nazw unikalnych. Na powtarzające się nazwy można sobie bezpiecznie pozwoliś przy lokalnych zmiennych procedur: nie grozi to praktycznie żadnymi konsekwencjami.

Zmienne statyczne

Zmienne modułowe i publiczne istnieją przez cały czas pracy programu. Natomiast zmienne deklarowane w procedurach są tworzone w momencie jej wywołania i usuwane z pamięci zaraz po jej zakończeniu. Kolejne wywołanie procedury inicjuje jej wszystkie zmienne od nowa.

W niektórych przypadkach wygodnie byłoby jednak zachować wartości lokalnych zmiennych procedury do jej kolejnego wywołania. Można to wprawdzie zastąpić zmienną publiczna albo modułową. Jednak do tego typu zmiennych mają dostęp wszystkie procedury i może się zdarzyć, że jej wartość zostanie gdzieś zmieniona. Pilnowanie tego typu zmiennych jest bardziej pracochłonne i czyni kod programu mniej czytelnym.

Sposobem na zachowanie wartości zmiennych lokalnych między kolejnymi wywołaniami procedury jest zadeklarowanie ich klauzulą Static zamiast Dim. Tak zadeklarowana zmienna zachowuje swoja wartość nawet po zakończeniu procedury i usunięciu z pamięci jej innych zmiennych lokalnych.

Oto przykład procedury służącej do sumowania ilości i zliczania wartości ogółem:

	Function Ogółem(Ile As integer) As Integer
	   Static Razem
	   Razem = Razem + Ile
	   Ogółem = Razem
	End Function
Jeżeli chcemy, aby wszystkie zmienne procedury lub funkcji były statyczne, to możemy użyć klauzuli Static w deklaracji procedury lub funkcji. Np:
	Static Function Ogółem(Ile As integer) As Integer
Spowoduje to, że każda zmienna - nawet te zadeklarowane klauzulami Dim i Private - będzie zmienną statyczna. Klauzula Static może być użyta w kazdej funkcji i procedurze łącznie z procedurami obsługo zdarzeń. Np.:
	Private Static Sub Form_Click()

Tablice

Tablica jest specyficzną struktura, która pozawala za pomocą jednej nazwy odwołać się do wielu zmiennych tego samego typu. Dostęp do każdego elementu tablicy jest możliwy poprzez podanie jego indeksu (numeru kolejnego) wewnątrz tablicy. W wielu sytuacja jest to bardzo pomocne i wygodne: zmniejsza ilość zmiennych, których nazwy musimy pamiętać, pozwala zastosować pętle do wykonania operacji na wszystkich elementach tablicy. Kod programu staje się przez to znacznie krótszy, a działanie programu bardziej efektywne.

Każda tablica posiada minimalny i maksymalny indeks, który musi być liczba całkowitą, a wszystkie elementy tablicy otrzymują kolejne numery między tymi indeksami. Należy pamiętać, aby nie rezerwować większych tablic, niż to jest konieczne: kompilator przydziela pamięć dla każdego elementu tablicy i tablica zarezerwowana "na wyrost" może się okazać bardzo pamięciochłonną i znacznie zwolnić działanie programu.

Uwaga: tablice zmiennych deklarowane w kodzie programu są zawsze indeksowane (numerowane) w sposób ciągły. Natomiast tablice kontrolek umieszczanych na formularzu nie muszą być tak numerowane. Ich indeksy nadajemy we właściwości Index każdej kontrolki, a to nie wymaga zachowania ciągłości.

Wszystkie elemeenty tablicy musza być tego samego typu. Jeżeli jednak jast to typ Variant, który pozwala na przechowywanie danych dowolnego typu, to w każdym elemencie możemy mieć inny rodzaj danych. Typ elementów tablicy może być dowolnym typem podstawowym lub zdefiniowanym przez użytkownika.

W Visual Basic'u istnieją 2 typy tablic:

  • stałej długości, które mają cały czas z góry zdefiniowaną ilość elementów;
     
  • dynamiczne, których rozmiar może ulegać zmianie podczas pracy programu (zobacz).
     

Tablice stałej długości

Tablice - analogicznie jak wszystkie inne zmienne - deklarujemy z użyciem któregoś z słów kluczowych Public, Private, Dim lub Static. Zasięg ich widoczności i czas życia automatycznie wynika ze sposobu deklaracji.

Deklarując tablicę musimy po jej nazwie podać w nawiasach okrągłych jej minimalny i maksymalny indeks. Oba indeksy muszą mieścić się w przedziale (-2 147 483 648 do 2 147 483 647) - liczba typu Long. Możemy podać w nawiasie tylko jedną liczbę: traktowana jest ona jako indeks górny, a wartość indeksu dolnego otrzymuje domyślną wartość 0. Np.:

	Dim Tab1(14) As Integer     ' tablica o 15 elementach
	                            ' z indeksami od 0 do 14
	Dim Tab2(99) As Integer     ' tablica o 100 elementach
	                            ' z indeksami od 0 do 99
Możemy sami zadeklarować nie tylko górny, ale i dolny indeks tworzonej tablicy. Np:
	Dim Tab1(1 To 10) As Integer     ' tablica o 10 elementach
	                                 ' z indeksami od 1 do 10
	Dim Tab2(-5 To 5) As Integer     ' tablica o 11 elementach
	                                 ' z indeksami od -5 do 5

Tablice wielowymiarowe

Czasami istnieje potrzeba zapamiętania bardziej złożonych danych, np.: oceny 25 uczniów z 10 przedmiotów. Możemy to zrobić deklarując tablice wielowymiarowe. W zasadzie ilość wymiarów tablicy jest nieograniczona. Jednak w praktyce wynika ona z ilości dostępnej pamięci, a tą tablice wykorzystują w postępie geometrycznym. Zobaczmy to przykładzie tablicy, która w każdym wymiarze ma 10 elementów typu Integer (2 bajty):
   Wymiarów          Elementów          Pamięć
      1                     10              20
      2                    100             200
      3                  1 000           2 000
      4                 10 000          20 000
      5                100 000         200 000
      6              1 000 000       2 000 000
      7             10 000 000      20 000 000
Pamiętając o tym należy z tablic wielowymiarowych korzystać, gdyż są bardzo przydatne, ale z rozsądkiem.

Sposób deklarowania tablic wielowymiarowych jest analogiczny do jednowymiarowych, z tym, że kolejne wymiary w deklaracji oddzielamy przecinkiem. Zakresy indeksów w każdym wymiarze są niezależne. Oto przykład kilku różnych deklaracji tablicy dwuwymiarowej o wymiarach 10 x10, których wszystkie elementy są typu wariant:

   Deklaracja                       Indeksy 1-go wymiaru     Indeksy 2-go wymiaru
   Dim Tab1(9, 9)                         0 do 9                   0 do 9
   Dim Tab1(9, 1 To 10)                   0 do 9                   1 do 10
   Dim Tab1(1 To 10, 9)                   1 do 10                  0 do 9
   Dim Tab1(1 To 10, 1 To 10)             1 do 10                  1 do 10
   Dim Tab1(5 To 14, -5 To 4)             5 do 14                 -5 do 4
W kodzie programu do tablicy odwołujemy się podając jej nazwę oraz w nawiasach okrągłych indeks (indeksy) elementu. Regułą jest bowiem praca z pojedynczym elementem tablicy. Oto przykład procedury, która zapisuje w tablicy dwuwymiarowej tabliczkę mnożenia:
	Sub Tabliczka()
	Dim i As Integer, j As Byte
	Dim MatrixA(1 To 10, 1 To 10) As Byte
	For i = 1 To 10
	   For j = 1 To 10
		  MatrixA(i, j) = i * j
	   Next
	Next
	End Sub

Tablice dynamiczne

Czasami zdarza się, że podczas pisania programu nie wiemy, jak dużej tablice będziemy potrzebowali. Wygodnie byłoby móc ustalić wymiar tablicy dopiero podczas pracy programu. do tego służą tablice dynamiczne, których rozmiar możemy zmienić (zwiększyć lub zmniejszyć) w każdym momencie pracy programu. Alternatywnym rozwiązaniem jest rezerwowanie tablic o maksymalnym możliwym rozmiarze. Jest to jednak marnotrawienie pamięci i znaczne wydłużenie pracy programu, które - najczęściej - nie jest niczym uzasadnione.

Tworzenie tablic dynamicznych

Deklarowanie tablic dynamicznych jest niemal identyczne z deklarowaniem tablic stałej długości. Jedyna różnica polega na tym, że w momencie deklarowania takiej tablicy nie podajemy jej rozmiaru pozostawiając puste nawiasy okrągłe. Np.:
	Dim Tab() As String
Jest to poprawna deklaracja tablicy, ale nie wystarcza do jej użycia. Wymaga wcześniejszego zadeklarowania jej wymiarów. Możemy z niej uczynić zarówno tablicę jedno- jak i wielowymiarową.

Do zainicjowania tablicy lub późniejszego zmienienia jej wielkości służy polecenie ReDim, w którym podajemy jej rozmiar. Np.:

	ReDim Tab(x+5)
Oczywiście polecenia ReDim możemy użyć tylko wewnątrz procedury lub funkcji, gdyż jest to instrukcja wykonywalna, ani deklaracja zmiennej. Można oczywiście instrukcja ReDim tworzyć dowolne zakresy indeksów i ilość wymiarów tablicy:
   Deklaracja                       Indeksy 1-go wymiaru     Indeksy 2-go wymiaru
   Dim Tab1(x) As Integer                 0 do x-1                 brak
   Dim Tab2(99) As Integer                0 do 99                   brak
   Dim Tab1(1 To 10) As Integer           1 do 10                   brak
   Dim Tab2(-5 To 5) As Integer          -5 do 5                    brak
   Dim Tab1(x, y)                         0 do x                   0 do y
   Dim Tab1(9, 1 To 10)                   0 do 9                   1 do 10
   Dim Tab1(1 To 10, 9)                   1 do 10                  0 do 9
   Dim Tab1(1 To 10, 1 To 10)             1 do 10                  1 do 10
   Dim Tab1(5 To 14, -5 To 4)             5 do 14                 -5 do 4

Zachowywanie zawartości tablicy

Każda zmiana rozmiaru tablicy powoduje jej ponowne zainicjowanie, a więc utratę wszystkich zapisanych w niej danych. Można temu zapobiec używając w poleceniu zmiany wielkości tablicy klauzulo Preserve. Np.:
	Dim Tab() As String
	...
	ReDim Tab(5)              ' 1-sza inicjacja tablicy
	...
	ReDim Preserve Tab(10)    ' powiększenie o 5 elementów
	                                 ' pierwsze 5 zachowa swoje wartości
Inicjując tablicę nadajemy jej elementom wartości domyślne:
  • Empty dla elementów typu Variant
     
  • 0 dla elementów każdego typu numerycznego
     
  • "" dla elementów typu String
     
  • Nothing dla elementów typu Object
     
Ponieważ w tablicy dynamicznej nie wiemy pisząc program jakie będą jej wymiary, a co za tym idzie nie znamy również zakresu jej indeksów. W tym momencie bardzo użyteczne stają się funkcje LBound i UBound, które zwracają odpowiednio najmniejszy i największy indeks tablicy. Można też je wykorzystać do zwiększenia rozmiaru tablicy o określoną ilość elementów.

W kolejnym przykładzie zwiększamy tablicę o 5 nowych elementów:

	' dla tablicy o indeksie początkowym 0
	ReDim Preserve Tab(UBound(Tab) + 5)
		lub
	' dla tablicy o dowolnym indeksie początkowym
	ReDim Preserve Tab(UBound(Tab) - LBound(Tab) + 6)
Uwaga: jeżeli używamy klauzuli Preserve w tablicach wielowymiarowych, to możemy zmienić tylko ostatni wymiar tablicy. Próba zmiany "wcześniejszych" wymiarów spowoduje błąd wykonania. Poprawną instrukcją jest zatem:
	ReDim Preserve Tab(UBound(Tab, 1), UBound(Tab, 2) + 10)
Ale błędna:
	ReDim Preserve Tab(UBound(Tab, 1) + 10, UBound(Tab, 2))

Stałe

Często zdarza się, że jakaś stała powtarza się bardzo często w kodzie programu lub wykonanie fragmentu kodu zależy od jakichś wartości, które wymagają zapamiętania, ale z niczym się nie kojarzą.

W takich przypadkach można uczynić kod programu bardziej czytelnym i zrozumiałem używając stałych. Stała jest to symboliczna nazwa, która reprezentuje pewną wartość numeryczna, łańcuch znaków lub jakieś wyrażenie. Ponieważ nazwę stałej nadajesz sam, to łatwiej jest ją zapamiętać niż jakieś wymyślne liczby czy wyrażenia. Natomiast w trakcie kompilacji programu wszystkie nazwy stałych zastępowane są przez przypisane do nich wartości czy wyrażenia.

Rozwiązanie takie posiada jeszcze inną zaletę. Jeżeli wartość stałej ulega zmianie, to wystarczy ją zmienić tylko w jednym miejscu. Nie ma potrzeby analizowania całego kodu programu i wyszukiwania wszystkich miejsc, gdzie ta wartość wystąpił. Zmiana wartości stałej spowoduje automatyczną zmianę wszystkich jej wystąpień

Istnieją dwa rodzaje stałych:

  • Predefiniowane stałe są dostarczane przez środowisko i kompilator. Visual Basic posiada cały szereg predefiniowanych stałych, które można wyszukać w przeglądarce obiektów. Wszystkie stałe środowiska Visual Basic oraz każdej dołączonej do projektu biblioteki są automatycznie dostępne dla programisty.
     
  • Symboliczne zwane stałymi użytkownika są stałymi zadeklarowanymi przez programistę w programie i dostępnymi tylko wewnątrz tego programu.
     

Predefiniowane stałe Visual Basic i Visual Basic for Applications mają nazwy rozpoczynające się przedrostkiem vb. Stałe z innych bibliotek na ogół również posiadają przedrostki pozwalające zorientować się z jakiej biblioteki pochodzą. Ten sposób nazewnictwa ma zapobiec ewentualnym kolizją z nazwami własnymi nadawanymi przez programistę oraz nazwami stałych z różnych bibliotek dołączonych do programu. Identyczne nazwy mogłyby spowodować w programie sporo bałaganu.

Aby być całkowicie pewnym, że nie nastąpi kolizja nazw, zawsze można odwoływać się do stałej z jej pełną nazwą kwalifikowaną:

	[NazwaBiblioteki.][NazwaModułu.]NazwaStałej
Jest to jednak dość pracochłonny sposób zabezpieczania się przed konfliktami nazw stałych i lepiej nadawać in rzeczywiście unikalne nazwy.

Tworzenie własnych stałych

Składnia deklaracji stałej jest następująca:

	[Public|Private] Const NazwaStałej [As typ] = wyrażenie
Gdzie:
  • NazwaStałej jest symboliczną nazwą nadawaną stałej. Reguły nadawania nazw stałym sa identyczne jak przy nadawaniu nazw zmiennym.
     
  • wyrażenie jest dowolnym poprawnym wyrażeniem języka Visual Basic składającym sie z liczb łańcuchów znaków i operatorów.
     

Przykładowe deklaracje własnych stałych w programie:

	Const conPi = 3.14159265358979
	Public Const conPlanet As Integer = 9
	Const conData = #6/5/1979#
	Public Const conAutor = "Jan Kowalski"

Wyrażenie po prawej stronie znaku równości jest najczęściej liczbą lub łańcuchem znaków. Może ono być jednak również wynikiem wykonania pewnych operacji lub odwołania się do innych zadeklarowanych stałych. Nie wolno jednak odwoływać się w deklaracji stałej do funkcji. Np:

	Const conPi2 = conPi * 2
	Const conInfo = conAutor & " urodzony " & conData 

Należy jednak pamiętać, aby wzajemne odwołania jednej zmiennej do drugiej nie tworzyły pętli cyklicznej, gdyż spowoduje to wystąpienie błędu wykonania. Poniższy przykład pokazuje sytuację, która nie jest poprawna i nie powinna wystąpić w programie:

	Public Const conA = conB * 2 
	Public Const conB = conA / 2 

Jeżeli już zdefiniowałeś stałą to możesz się do niej odwołać w dowolnym miejscu programu. Tak zapisany kod jest bardziej czytelny. Np.:

	SystemSłoneczny(1 To conPlanet)
	If x > conPi2 Then Exit Sub

Zasięg widoczności stałych

Deklaracja Const ma zasięg widoczności stałych i reguły identyczne jak deklaracje zmiennych:

  • Stała zadeklarowana wewnątrz procedury jest widoczna tylko w tej procedurze.
     
  • Stała zadeklarowana w części deklaracyjnej modułu jest widziana we wszystkich procedurach modułu, w którym została zadeklarowana.
     
  • Żeby stała była widziana w całym programie musi być zadeklarowana w części deklaracyjnej modułu z użyciem klauzuli Public. Przy czym deklaracja musi znajdować się w module kodu. Nie jest dopuszczalna deklaracja publicznych stałych w module formularza ani w module klasy.
     

Konwencje nazw zmiennych i stałych

Każdy praktycznie program używa jakichś zmiennych. Często również własnych stałych. W małych aplikacjach nie stanowi to na ogół problemu. W dużych, gdy ilość zmiennych i stałych idzie w setki , a często w tysiące, trudno jest zapanować nad wszystkimi nazwami zmiennych i stałych. Dlatego przyjęło się używać pewnej konwencji nadawania zmiennym i stałym nazw. Ma ona ułatwić poruszanie sie w gąszczu nazw. Nie jest ona oczywiście obowiązkowa i w praktyce możesz nazywać swije zmienne w dowolny sposób. Zachowanie jednak pewnych konwencji znacznie ułatwia pisanie i testowanie oraz ewentualne zmiany w programie: kod jest czytelniejszy i łatwiejszy do zrozumienia.

Przyjmując zachowanie konwencji, każdy programista może storzyć oczywiście własne. Przedstawione tutaj stanowią jednak pewien standard, choć wcale nie muszą byc bezwzglednie zachowane.

Ogólny schemat nadawania nazw zmiennym można przedstawić następująco:

	PrzedrostekZakresu PrzedrostekTypu NazwaWłasna

Gdzie:

  • PrzedrostekZakresu jest pojedynczym znakiem sygnalizującym zakres widoczności zmiennej.
     
  • PrzedrostekTypu to trzyznakowy symbol typu danych przechowywanego w zmiennej.
     
  • NazwaWłasna to nazwa nadana zmiennej przez programistę. Wskazane jest, aby nazwa sugerowała co w zmiennej jest przechowywane. Kod programu staje się dzięki temu czytelniejszy. Patrząc na dwa zapisy działania:
    		gcurWartość = intIlość * sngCena
    		i
    		X = Y * Z
    	
    o wiele łatwiej zorientować co jest wyliczane w pierwszym aniżeli w drugim przypadku, choć oba zapisy dają w sumie ten sam wynik.
     

Przedrostek zasięgu

Ponieważ zmienne i stałe zawsze są widziane w jakimś określonym zasięgu programu, to dobrze jest w ich nzawie zasygnalizować z jakiego typu zmienną (stałą) mamy do czynienia. Przyjętą w tym zakresie konwencję pokazuje poniższa tabela:
Zakres Deklaracja Widoczny w: Prefiks Przykład
Lokalny Private/Dim w procedurze lub funkcji procedurze/funkcji, w któej wystąpiła deklaracja - curCena
Poziom modułu Private/Dim w deklaracyjnej części modułu (.frm, .bas) kazdej procedurze modułu m mblnOK
Globalny Public w module kodu (.bas) całej aplikacji g gstrNazwisko

Przedrostek typu

Przedrostek typu to trzyznakowy łańcuch informujący o typie danych przechowywanych w zmiennej. ułatwnia to czytanie kodu oraz kontrolę nad tym, co do danej zmiennej mozemy przypisac, a czego nie. Przykęte konwencje używania przedrostków typu przedstawia poniższa tabela:
Typ danychPrefiksPrzykład
BooleanblnblnOK
BytebytbytMiesiąc
CollectioncolcolOsoby
CurrencycurcurCena
Date (Time)dtmdtmData
DoubledbldblIlość
ErrorerrerrNrBłędu
IntegerintintWiek
LonglnglngOdległość
ObjectobjobjFormularz
SinglesngsngŚrednia
StringstrstrImię
typ użytkownikadutudtMójTyp
VariantvntvntCośTam

Nazwy własne

Nazwy własne nie podlegają żadnym konwencjom. Należy jednak pamiętac, zę dobrym obyczajem jest nadawanie nazw, które o czymkolwiek mówia. Dotyczy to zarówno zmiennyhc, stałych jak i nazw tworzonych przez peogramistę procedur i funkcji. Zobaczmy dwa fragmenty kodu programu:
	'Przykład 1:
	Function WartośćOgółem(intIlość As Integer, curCena As Currency)
	Static curWartośćOgółem As Currency
	curWartośćOgółem = curWartośćOgółem + intIlość * curCena
	WartośćOgółem = curWartośćOgółem
	End Function
	
	'Przykład 2:
	Function wo(i As Integer, c As Currency)
	Static o As Currency
	o = o + i * c
	wo = o
	End Function
Oba przykłady wykonują dokładnie to samo. Drugi z nich wymaga na pewno mniej pracy podczas pisania kodu. Jednak patrząc na pierwszy przykład od razu wiemy, o co w nim chodzi. Nie wymaga on żadnego komentarza. Drugi natomiast nic nam nie mówi. Sądzę, że sam autor takiego kodu wracając do niego po kilkutygodniowej przerwie musiałby się dobrze zastanowic, co miał na myśli pisząc te instrukcje. Pierszy kod jest na pewno bardziej pracochłonny, ale nakład pracy na pewno się opłaca.
« wstecz   dalej »