SlideShare a Scribd company logo
IDZ DO
         PRZYK£ADOWY ROZDZIA£

                           SPIS TRE CI   VB .NET. Almanach
                                         Autorzy: Steve Roman, Ron Petrusha, Paul Lomax
           KATALOG KSI¥¯EK               T³umaczenie: Dorota Bednarz,
                                         Krzysztof Jurczyk, Dariusz Ma³yszko
                      KATALOG ONLINE     ISBN: 83-7197-730-1
                                         Tytu³ orygina³u: VB .NET Language In A Nutshell
       ZAMÓW DRUKOWANY KATALOG           Format: B5, stron: 754


              TWÓJ KOSZYK
                    DODAJ DO KOSZYKA     W dziesiêæ lat po powstaniu jêzyka Visual Basic firma Microsoft wprowadza na rynek
                                         platformê .NET z ca³kowicie poprawion¹ i przebudowan¹ wersj¹ tego jêzyka, opatrzon¹
                                         nazw¹ Visual Basic .NET. Zdaniem niektórych jest to ca³kiem nowy jêzyk
         CENNIK I INFORMACJE             programowania. Visual Basic jest teraz w pe³nym tego s³owa znaczeniu jêzykiem
                                         zorientowanym obiektowo — z d³ugo oczekiwanym dziedziczeniem klas i innymi
                   ZAMÓW INFORMACJE      elementami charakteryzuj¹cymi programowanie obiektowe.
                     O NOWO CIACH        W wiêkszo ci ksi¹¿ek po wiêconych Visual Basicowi zak³ada siê, ¿e czytelnik jest
                                         ca³kowitym nowicjuszem w dziedzinie programowania i dlatego s¹ one w du¿ej czê ci
                       ZAMÓW CENNIK      po wiêcone wprowadzeniu go w takie pojêcia, jak zmienne, ³añcuchy i instrukcje.
                                         Niniejszy almanach jest zupe³nie innym rodzajem ksi¹¿ki. Stanowi szczegó³owe,
                                         profesjonalne ród³o informacji o jêzyku VB .NET, do którego mo¿na siê odwo³aæ,
                 CZYTELNIA               by od wie¿yæ informacje na temat konkretnego elementu jêzyka czy parametru.
                                         Ksi¹¿ka bêdzie doskona³¹ pomoc¹ podczas programowania, kiedy zaistnieje potrzeba
          FRAGMENTY KSI¥¯EK ONLINE       przejrzenia regu³ dotycz¹cych stosowania konkretnego elementu sk³adowego jêzyka
                                         lub wtedy, gdy nale¿y sprawdziæ czy nie przeoczono jakiego istotnego szczegó³u
                                         zwi¹zanego z konkretnym elementem jêzyka.
                                         W ksi¹¿ce VB .NET. Almanach omówiono m.in.:
                                            • Podstawowe typy danych jêzyka Visual Basic oraz sposób ich wykorzystania,
                                              a tak¿e typy danych .NET
                                            • Programowanie obiektowe w VB .NET
                                            • Nowe elementy sk³adowe .NET Framework, maj¹ce najwiêkszy wp³yw na sposób
                                              programowania w VB .NET, bibliotekê klas .NET Framework
                                            • Delegacje, zdarzenia i obs³ugê b³êdów w VB .NET
                                            • Wszystkie funkcje, instrukcje, dyrektywy, obiekty i elementy sk³adowe obiektów
Wydawnictwo Helion                            tworz¹ce jêzyk VB .NET
ul. Chopina 6                               • Pu³apki czyhaj¹ce na programistê VB .NET i wiele przydatnych „tricków”
44-100 Gliwice                                programistycznych
tel. (32)230-98-63
e-mail: helion@helion.pl
5RKU VTG EK

Wstęp ....................................................................................................................... 5

Część I Podstawy...............................................................................13
Rozdział 1. Wprowadzenie................................................................................... 15
      Dlaczego Visual Basic .NET? ................................................................................................... 16
      Czym jest VB .NET? ................................................................................................................. 20
      Co mogę zrobić w VB .NET? ................................................................................................... 26

Rozdział 2. Zmienne i typy danych ..................................................................... 27
      Zmienne ................................................................................................................................... 27
      Deklaracje zmiennych i stałych ................................................................................................ 32
      Typy danych............................................................................................................................. 34
      Tablice ...................................................................................................................................... 51
      Zmienne obiektowe i ich wiązanie........................................................................................... 55
      Obiekt Collection...................................................................................................................... 57
      Parametry i argumenty ............................................................................................................ 58

Rozdział 3. Wprowadzenie do programowania obiektowego ............................ 65
      Dlaczego programowanie obiektowe?..................................................................................... 65
      Podstawy programowania obiektowego ................................................................................. 66
      Klasy i obiekty.......................................................................................................................... 71
      Dziedziczenie ........................................................................................................................... 78
      Interfejsy, abstrakcyjne składowe i klasy ................................................................................. 84
      Polimorfizm i przeciążanie....................................................................................................... 87
      Zasięg i dostęp w module klasy .............................................................................................. 88
4                                                                                                                                    Spis treści


Rozdział 4. .NET Framework — podstawowe pojęcia ....................................... 91
      Przestrzenie nazw .................................................................................................................... 91
      CLR (Common Language Runtime), kod zarządzany i dane zarządzane............................... 92
      Nadzorowane wykonanie ........................................................................................................ 93
      Pakiety...................................................................................................................................... 94
      Pakiety a VB .NET.................................................................................................................... 95

Rozdział 5. Biblioteka klas .NET Framework..................................................... 99
      Przestrzeń nazw System ........................................................................................................ 100
      Pozostałe przestrzenie nazw .................................................................................................. 106

Rozdział 6. Delegacje i zdarzenia ...................................................................... 113
      Delegacje ................................................................................................................................ 113
      Zdarzenia i ich wiązanie ........................................................................................................ 117

Rozdział 7. Obsługa błędów w VB .NET........................................................... 121
      Wykrywanie i obsługa błędów .............................................................................................. 121
      Obsługa błędów czasu wykonania......................................................................................... 122
      Obsługa błędów logicznych ................................................................................................... 131
      Kody błędów.......................................................................................................................... 133


Część II Leksykon............................................................................. 135
Rozdział 8. Słownik języka VB .NET ................................................................ 137

Część III Dodatki ............................................................................. 661
Dodatek A Nowości i zmiany w VB .NET ........................................................ 663

Dodatek B Elementy języka — podział na kategorie ....................................... 681

Dodatek C Operatory ......................................................................................... 699

Dodatek D Stałe i wyliczenia ............................................................................ 709

Dodatek E Kompilator VB .NET uruchamiany z wiersza poleceń................... 719

Dodatek F Elementy języka VB 6 nieobsługiwane przez VB .NET .................. 727

Skorowidz ............................................................................................................ 731
3
                                                Wprowadzenie
                                            do programowania
                                                  obiektowego

Ten rozdział jest krótkim i zwięzłym wprowadzeniem do programowania obiektowego.
Ponieważ nie jest to książka o programowaniu obiektowym, skupimy się na tych zagad-
nieniach, które są ważne podczas programowania w VB .NET.


Dlaczego programowanie obiektowe?
Począwszy od wersji 4 Visual Basic umożliwia stosowanie szeregu technik programowania
obiektowego. Jednak niejednokrotnie prezentowano pogląd, że dotychczasowe wersje
języka Visual Basic nie są „prawdziwym” obiektowym językiem. Dopiero w VB .NET
zmiany wprowadzone w dziedzinie obiektowości są naprawdę zauważalne. Bez względu
na prezentowane w tej kwestii stanowisko wydaje się oczywiste, że VB .NET jest obiek-
towym językiem programowania w pełnym tego słowa znaczeniu.

Można w tym miejscu powiedzieć: „Nie chcę używać technik programowania obiektowego
w moich programach.”. W przypadku VB 6 było to jeszcze możliwe. Jednak w VB .NET
struktura .NET Framework — szczególnie biblioteka klas Base Class Library — jak
również dokumentacja jest całkowicie zorientowana obiektowo. Z tego powodu nie
można dłużej unikać możliwości poznania podstaw programowania obiektowego nawet
wtedy, gdy zdecydujemy się nie używać tych technik we własnych aplikacjach.
66                                Rozdział 3. Wprowadzenie do programowania obiektowego


Podstawy programowania obiektowego
W literaturze często podaje się, że u podstaw programowania obiektowego leżą cztery
główne pojęcia:
• hermetyzacja;
• abstrakcja;
• dziedziczenie;
• polimorfizm.

Każde z powyższych pojęć w charakterystyczny dla siebie sposób odgrywa znaczącą
rolę w programowaniu w VB .NET. Hermetyzacja oraz abstrakcja są „teoretycznymi”
pojęciami stanowiącymi podstawę programowania obiektowego. Dziedziczenie i poli-
morfizm stanowią pojęcia bezpośrednio stosowane podczas programowania w VB .NET.

Abstrakcja
Pojęcie abstrakcja oznacza po prostu przedstawienie danego elementu — encji — zawie-
rające jedynie te jego aspekty, które są ważne w konkretnej sytuacji. Przypuśćmy, że chcemy
utworzyć komponent odpowiedzialny za przechowywanie informacji o pracownikach
przedsiębiorstwa. W tym celu rozpoczniemy od utworzenia listy pozycji istotnych dla
naszej encji (pracownika przedsiębiorstwa). Niektóre z tych pozycji to:
• imię i nazwisko;
• adres;
• numer identyfikacyjny pracownika;
• pobory;
• zwiększenie poborów;
• zmniejszenie poborów.

Ważne jest, że dołączyliśmy nie tylko właściwości encji (tj. pracowników), takie jak imię
i nazwisko, ale również akcje, które możemy wykonać na tych encjach, jak na przykład
zwiększenie lub zmniejszenie poborów. Wymienione czynności lub działania nazywane
są również metodami, operacjami lub zachowaniami. W tej książce będziemy używać
terminu metody, który jest powszechnie stosowany w VB .NET.

Oczywiście nie będziemy tworzyć właściwości IQ określającej iloraz inteligencji pracow-
nika, ponieważ nie jest to stosowne (by nie wspomnieć o dyskryminacyjnym charakterze
takiego postępowania). Nie będziemy również dołączać właściwości „kolor włosów.”
Wprawdzie ta cecha wchodzi w skład encji, jednak nie jest w tym przypadku istotna.

Podsumowując — utworzyliśmy abstrakcyjne pojęcie pracownika, które zawiera jedynie
te właściwości i metody, które nas interesują. Po utworzeniu takiego abstrakcyjnego modelu
można przystąpić do hermetyzacji jego właściwości i metod w konkretnym komponencie.
Podstawy programowania obiektowego                                                    67


Hermetyzacja
Hermetyzacja oznacza zawieranie właściwości i metod danego abstrakcyjnego modelu
i udostępnianie na zewnątrz jedynie tych z nich, które są absolutnie konieczne. Każda
właściwość i metoda modelu abstrakcyjnego nazywana jest jego elementem. Zbiór udo-
stępnianych na zewnątrz elementów składowych modelu abstrakcyjnego (lub kompo-
nentu zawierającego model abstrakcyjny) określa się zbiorowym terminem interfejsu
publicznego (lub po prostu interfejsu).

Hermetyzacja spełnia trzy główne zadania:
• umożliwia ochronę właściwości i metod przed dostępem z zewnątrz;
• umożliwia kontrolę poprawności wprowadzanych danych w interfejsie publicznym
  (na przykład sprawdzenie tego, czy nie zostaje przypisana zarobkom pracownika
  ujemna liczba);
• zwalnia użytkownika od konieczności wnikania w szczegóły implementacyjne
  właściwości i metod.

Weźmy jako przykład typ danych Integer, do którego dostęp jest starannie kontrolo-
wany przez VB. Z pewnością wiadomo, że liczba całkowita jest przechowywana w pamięci
komputera w postaci binarnego ciągu zer i jedynek. W VB liczby całkowite reprezentowane
są w formie dopełnienia do dwu, dzięki czemu możliwe jest przechowywanie liczb ujem-
nych i dodatnich.

W celu uproszczenia rozważań weźmy pod uwagę 8-bitowe liczby całkowite. Liczba
całkowita 8-bitowa ma postać a7a6a5a4a3a2a1a0, gdzie każdy element a o danym indeksie
jest zerem lub jedynką. Można podać następującą reprezentację graficzną takiej liczby:




Rysunek 3.1. 8-bitowa liczba binarna

W liczbie, która zostanie zapisana w postaci dopełnienia do dwu, bit znajdujący się naj-
bardziej na lewo — a7 (nazywany również najbardziej znaczącym bitem) — jest bitem
znaku. Jeżeli bit znaku zawiera wartość 1, to liczba jest ujemna. W przeciwnym razie,
gdy bit znaku zawiera 0, liczba jest dodatnia.

Przy zamianie liczby a7a6a5a4a3a2a1a0 zapisanej w postaci dopełnienia do dwu na postać
dziesiętną stosowany jest następujący wzór:

postać dziesiętna = –128a7 + 64a6 + 32a5 + 16a4 + 8a3 + 4a2 + 2a1 + a0

Utworzenie liczby o przeciwnym znaku do danej liczby, która jest zapisana w postaci
dopełnienia do dwu, polega na zmianie wartości każdego bitu na wartość przeciwną
(tzn. każde 0 zamieniamy na 1, a każde 1 na 0), po czym do powstałej liczby dodaje się 1.
68                                Rozdział 3. Wprowadzenie do programowania obiektowego


W tym miejscu można powiedzieć: „Jako programista nie muszę zaprzątać sobie głowy
takimi szczegółami. Wystarczy, że napiszę:
     x = -16
     y = -x

a kompilator niech wybierze odpowiednią reprezentację liczby i wykona wymagane
operacje.”.

Właśnie o to chodzi w hermetyzacji. Szczegóły interpretacji przez komputer (i kompilator)
liczb całkowitych ze znakiem oraz implementacja ich właściwości i operacji na nich wy-
konywanych są hermetyzowane, czyli zamknięte w samym typie całkowitoliczbowym.
W ten sposób powyższe informacje są przed użytkownikami tego typu ukryte. Mamy
dostęp jedynie do tych właściwości i operacji, które są potrzebne do posługiwania się
liczbami całkowitymi. Udostępniane na zewnątrz właściwości i metody tworzą publiczny
interfejs dla typu Integer.

Ponadto hermetyzacja chroni przed popełnianiem błędów. Powróćmy jeszcze na mo-
ment do przedstawionego powyżej przykładu — jeżeli musielibyśmy sami zmienić znak
liczby tworząc dopełnienia do dwu i na końcu dodając 1, moglibyśmy zapomnieć wy-
konać którąś z tych operacji. Hermetyzowany typ danych sam sprawuje automatycznie
nad tym kontrolę.

Hermetyzacja posiada jeszcze jedną ważną cechę. Kod napisany z wykorzystaniem udo-
stępnianego na zewnątrz interfejsu pozostaje aktualny — nawet po zmianie wewnętrz-
nych mechanizmów implementacji typu Integer — tak długo, jak długo ten interfejs nie
ulega zmianie. Jeżeli przeniesiemy teraz nasz kod do komputera, który przechowuje liczby
całkowite w postaci dopełnienia do jednego, wtedy wewnętrzna procedura, która imple-
mentuje operację zmiany znaku liczby całkowitej, będzie musiała się zmienić. Z punktu
widzenia programisty nic się jednak nie zmienia. Poniższy fragment kodu:

     x = -16
     y = -x

jest nadal poprawny.


Interfejsy
W VB hermetyzacja realizowana jest poprzez tworzenie komponentów. Można utworzyć
komponent hermetyzujący omawiany wcześniej abstrakcyjny modelu pracownika.

W VB .NET realizację metod interfejsu stanowią funkcje. Natomiast każda z właściwości,
jak zobaczymy w dalszej części tego rozdziału, jest implementowana jako prywatna
zmienna z dwiema towarzyszącymi publicznymi funkcjami. Zmienna prywatna prze-
chowuje wartość właściwości. Pierwsza z funkcji publicznych służy do pobierania war-
tości właściwości, podczas gdy druga wykorzystywana jest do jej ustawiania. Wymie-
nione dwie funkcje określa się czasami mianem metod udostępniających właściwości. Zbiór
udostępnianych na zewnątrz funkcji (zwykłych metod oraz metod udostępniających)
tworzy interfejs modelu abstrakcyjnego.
Podstawy programowania obiektowego                                                    69


Komponent może hermetyzować i udostępniać na zewnątrz więcej niż jeden model abs-
trakcyjny (a zatem więcej niż jeden interfejs). Bardziej realistycznym przykładem może
być komponent wzorujący się na pracownikach przedsiębiorstwa, który posiada interfejs
IIdentification (pierwsza litera „I” jest skrótem od słowa interfejs) służący do celów
identyfikacji. Wspomniany interfejs mógłby mieć następujące właściwości: imię i nazwi-
sko, numer ubezpieczenia, numer prawa jazdy, wiek, znaki szczególne itd. Poza tym
interfejsem, komponent mógłby również zawierać interfejs zwany IEducation opisujący
wykształcenie pracownika. Ten drugi interfejs implementowałby takie właściwości jak:
poziom wykształcenia, tytuły, ukończone szkoły itp.

Interfejs każdego modelu abstrakcyjnego udostępnianego przez komponent określa się
również jako interfejs komponentu. W ten sposób komponent Employee (pracownik) im-
plementuje przynajmniej dwa interfejsy: IIdentification i IEducation. Należy
pamiętać, że termin interfejs jest również używany do określenia zbioru wszystkich udo-
stępnianych na zewnątrz właściwości i metod komponentu (w tym przypadku kompo-
nent ma tylko jeden interfejs).

Wracając do naszego abstrakcyjnego modelu opisującego pracownika — jego interfejs
mógłby składać się z funkcji zaprezentowanych w tabeli 3.1. Podany interfejs jest oczywi-
ście bardzo uproszczony, ale w zupełności wystarcza do zilustrowania omawianych pojęć.

Tabela 3.1. Elementy składowe interfejsu Employee (pracownik)

 Typ                   Nazwa
 Właściwość            FullName: GetFullName(), SetFullName()
 Właściwość            Address: GetAdress(), SetAddres()
 Właściwość            EmployeeID: GetEmployeeID(), SetEmployeeID()
 Właściwość            Salary: GetSalary(), SetSalary()
 Metoda                IncSalary()
 Metoda                DecSalary()

Chociaż używanie terminu interfejs w znaczeniu zbioru funkcji jest powszechne, to wiąże
się jednak z pewnym problemem. Mianowicie chodzi o to, że podanie nazw wszystkich
funkcji interfejsu (w sposób przedstawiony w tabeli) nie dostarcza pełnej informacji po-
trzebnej do wywołania tych funkcji. Bardziej użyteczną definicją interfejsu jest podanie
zbioru sygnatur publicznych funkcji komponentu.

W celu wyjaśnienia tego zagadnienia przeanalizujemy jedno z najważniejszych rozgraniczeń
w programowaniu obiektowym — rozróżnienie między deklaracją, a implementacją funkcji.

Utwórzmy następującą funkcję sortującą:

     Function Sort(a() As Integer, iSize As Integer) As Boolean
       For i = 1 to iSize
         For j = i + 1 to isize
           If a(j) < a(i) Then swap a (i), a(j)
70                                 Rozdział 3. Wprowadzenie do programowania obiektowego

         Next j
       Next i
       Sort = True
     End Function

Pierwszy wiersz kodu:

     Function Sort(a()   As Integer, iSize As Integer) As Boolean

jest deklaracją funkcji. Deklaracja funkcji zawiera informację o liczbie i typie pobieranych
parametrów oraz o typie zwracanej przez funkcję wartości. Treść funkcji:

     For i = 1 to iSize
       For j = i + 1 to iSize
         If a(j) < a(i) Then swap a (i), a(j)
       Next j
     Next i
     Sort = True

jest implementacją funkcji. Opisuje, jak funkcja wykonuje postawione przed nią zadania.

Należy zauważyć, że jest możliwa zmiana sposobu implementacji funkcji bez zmiany
deklaracji funkcji. W rzeczywistości podana implementacja funkcji sortuje tablicę a za
pomocą prostego algorytmu sortowania przez selekcję, ale możemy zmienić ten algo-
rytm na dowolnie wybrany, inny algorytm sortowania (sortowanie bąbelkowe, sortowa-
nie przez wstawianie, sortowanie szybkie — quick sort itp.)

Teraz przyjrzyjmy się programowi (klientowi), który ma wykorzystać funkcję Sort.
Program wywołujący musi znać jedynie deklarację funkcji Sort, by móc ją wywołać.
Nie powinien (a prawdopodobnie nie chce) znać szczegółów implementacyjnych. W ten
sposób to deklaracja funkcji — a nie jej implementacja — tworzy interfejs funkcji

Sygnatura funkcji jest nazwą funkcji i typem zwracanej przez nią wartości wraz z na-
zwami i typami jej parametrów podanymi w prawidłowej kolejności. Deklaracja funkcji
jest po prostu przejrzystym sposobem opisania sygnatury funkcji. Zgodnie z konwencją
przyjętą przez Microsoft wartość zwracana przez funkcję nie jest częścią sygnatury
funkcji. Wedle tej konwencji sygnatura jest tzw. sygnaturą argumentów. Przyczyny przy-
jęcia takiej konwencji staną się bardziej zrozumiałe w dalszej części rozdziału, kiedy
przejdziemy do omawiania przeciążania nazw. Jednak byłoby lepiej (jak zwykle), gdyby
terminologia stosowana przez Microsoft była bardziej starannie przemyślana.

Według tej specyficznej definicji interfejsu interfejs naszego komponentu Employee
(pracownik) mógłby wyglądać następująco (przedstawiono jedynie jego część):

     Function GetFullName(iEmpID As Long) As String
     Sub SetFullName(lEmpID As Long, sName As String)
             …
     Sub IncSalary(sngPercent As Single)
     Sub decSalary(sngPercent As Single)
Klasy i obiekty                                                                      71


Klasy i obiekty
Klasa jest komponentem definiującym i implementującym jeden lub więcej interfejsów.
Ściśle mówiąc — klasa nie musi implementować wszystkich składowych interfejsu
(omówione to zostanie podczas opisu składowych abstrakcyjnych klasy). Wyrażając to ina-
czej można powiedzieć, że klasa łączy dane, funkcje i typy w jeden nowy typ. Microsoft
używa pojęcia typ również w odniesieniu do klas.

Moduły klas VB .NET
W Visual Studio.NET moduł klas VB wstawiany jest do projektu po wybraniu pozycji
Add Class z menu Projekt. W ten sposób dołączony zostaje nowy moduł zawierający kod:
    Public Class ClassName

    End Class

Visual Studio przechowuje każdą klasę w oddzielnym pliku, jednak nie jest to konieczne.
Konstrukcja Class ...End Class zaznacza początek i koniec definicji klasy. W ten
sposób w pojedynczym pliku źródłowym może znajdować się więcej niż jedna klasa,
jak też może zostać umieszczonych jeden lub więcej modułów (ograniczonych konstrukcją
Module...End Module).

Klasa CPerson zdefiniowana w następnym podrozdziale jest przykładem wykorzystania
modułu klas.

Składowe klasy
W VB .NET moduły klas mogą zawierać przedstawione niżej rodzaje elementów.

Dane
  Znajdują się tutaj zmienne (również określane jako pola) oraz stałe.

Zdarzenia
   Zdarzenia są procedurami wywoływanymi automatycznie przez Common
   Language Runtime w odpowiedzi na niektóre zachodzące akcje (takie jak
   na przykład tworzenie obiektu, naciśnięcie przycisku, zmiana danych lub wyjście
   obiektu z zasięgu).

Funkcje i procedury
   Funkcje i procedury nazywane są również metodami. Konstruktor klasy
   jest specjalnym rodzajem metody. Konstruktory są szczegółowo omawiane
   w dalszej części tego rozdziału.

Właściwości
  Właściwość jest implementowana jako prywatna zmienna wraz z dwiema
  specjalnymi funkcjami udostępniającymi. Składnia tych specjalnych funkcji
  jest omówiona w dalszej części tego rozdziału w podrozdziale „Właściwości.”
72                                Rozdział 3. Wprowadzenie do programowania obiektowego


Typy
   Składowa klasy może być również inną klasą (w tym przypadku określana jest
   jako klasa zagnieżdżona).

Poniższa klasa CPerson zawiera kilka rodzajów składowych:

     Public Class CPerson
     ' -----------------
     ' Dane
     ' -----------------
     ' Zmienne składowe
       Private msName As String
       Private miAge As Integer

     ' Stała
       Public Const MAXAGE As Short = 120

     ' Zdarzenie
       Public Event Testing()

     ' -----------------
     ' Funkcje
     ' -----------------

     ' Metody
       Public Sub Test()
         RaiseEvent Testing ()
       End Sub

       Property Age() As Integer
         Get
           Age = miAge
         End Get
         Set(ByVal Value As Integer)
           ' Kontrola poprawności
           If Value < 0 Then
             MsgBox("Wiek nie mo e być liczbą ujemną")
           Else
             miAge = Value
           End If
         End Set
       End Property

     ' Właściwość
       Property Name() As String
         ' Metody udostępniające właściwości
         Get
           Name = msName
         End Get
         Set(ByVal Value As String)
           msName = Value
         End Set
       End Property

       Sub Dispose()
         ' Zwolnienie zasobów
       End Sub
     End Class
Klasy i obiekty                                                                      73


Interfejs publiczny klasy VB .NET
Podczas omawiania pojęć związanych z programowaniem obiektowym stwierdziliśmy,
że udostępniane na zewnątrz składowe komponentu tworzą jego publiczny interfejs
(lub po prostu interfejs). Ponadto w VB .NET każda składowa modułu klasy ma określony
typ dostępu, którym może być Public, Private, Friend, Protected lub Protected
Friend. Typy dostępu zostały szczegółowo omówione w dalszej części tego rozdziału.
W tym miejscu wystarczy powiedzieć, że moduł klasy w VB .NET może mieć składowe
typu Public, Private, Friend, Protected oraz Protected Friend.

W ten sposób powstaje pewna dwuznaczność przy definiowaniu pojęcia interfejsu pu-
blicznego klasy w VB .NET. Samo pojęcie mogłoby wskazywać, że należy uważać każdą
składową udostępnianą na zewnątrz jako część publicznego interfejsu klasy. W tym
przypadku pod pojęciem interfejsu publicznego klasy rozumielibyśmy oprócz składowych
Public również składowe Protected, Friend oraz Protected Friend. Z drugiej
strony — można by argumentować, że składowe interfejsu publicznego muszą być udo-
stępniane na zewnątrz projektu zawierającego klasę, do którego omawiany interfejs na-
leży. Przy takim zdefiniowaniu pojęcia interfejsem publicznym są jedynie jego składowe
Public. Na szczęście nie musimy podawać dokładnej definicji interfejsu publicznego
klasy w VB .NET, jeśli pamiętamy, że ten termin może być różnie definiowany.


Obiekty
Klasa przedstawia jedynie opis właściwości oraz metod, a tym samym nie jest jednostką
posiadającą samodzielny byt (z wyjątkiem składowych współużytkowanych, które zostaną
omówione w dalszej części książki.) W celu wywołania metod i wykorzystania właści-
wości należy utworzyć instancję klasy, oficjalnie nazywaną obiektem. Tworzenie instancji
klasy określane jest jako konkretyzacja lub ukonkretnienie klasy.

Istnieją trzy sposoby utworzenia obiektu danej klasy w VB .NET. Jeden z nich polega
na zadeklarowaniu zmiennej obiektowej reprezentującej daną klasę:

       Dim APerson As CPerson

a następnie utworzeniu obiektu za pomocą słowa kluczowego New:

       APerson = New CPerson()

Możemy obie czynności wykonać w jednym wierszu kodu:

       Dim APerson As New CPerson()

lub:

       Dim APerson As CPerson = New CPerson()

Pierwszy sposób zapisu uważany jest za skróconą formę drugiego zapisu.
74                                Rozdział 3. Wprowadzenie do programowania obiektowego


Właściwości
Właściwości są składowymi klasy, które mogą być zaimplementowane na dwa różne
sposoby. W najprostszej postaci właściwość jest jedynie publiczną zmienną:

     Public Class CPerson
       Public Age As Integer
     End Class

W przypadku takiej implementacji właściwości Age problemem jest brak hermetyzacji.
Dostęp do obiektu klasy CPerson daje możliwość ustawienia właściwości Age na do-
wolną wartość typu Integer (w tym również na wartość ujemną, która jest nieprawi-
dłową reprezentacją wieku). Nie ma tutaj możliwości kontrolowania poprawności wpro-
wadzanych danych (a ponadto powyższa implementacja właściwości nie umożliwia jej
włączenia do interfejsu publicznego klasy w myśl podanej przez nas definicji tego terminu).

„Poprawnym” obiektowym sposobem implementacji właściwości jest utworzenie
zmiennej typu Private wraz z dwiema funkcjami. Zmienna typu Private przecho-
wuje wartość właściwości. Natomiast dwie funkcje składowe, które są zwane metodami
udostępniającymi, służą do pobierania i ustawiania wartości właściwości. W ten sposób
dane są hermetyzowane, a dostęp do właściwości może zostać ograniczony za pomocą
funkcji udostępniających, które mogą kontrolować poprawność wprowadzanych danych.
Poniższy fragment kodu implementuje właściwość Age:
     Private miAge As Integer
     Property Age() As Integer
       Get
         Age = miAge
       End Get
       Set(ByVal Value As Integer)
         ' Kontrola poprawności wprowadzanych danych
         If Value < 0 Then
           MsgBox("Wiek musi być liczbą dodatnią")
         Else
           miAge = Value
         End If
       End Set
     End Property

Jak wynika z powyższego przykładu, Visual Basic ma specjalną składnię służącą do defi-
niowania metod udostępniających właściwości. Natychmiast po wprowadzeniu w edytorze
Visual Studio .NET poniższego wiersza kodu:
     Property Age() As Integer

tworzony jest przez IDE następujący wzorzec:
     Property Age() As Integer
       Get

       End Get
       Set(ByVal Value As Integer)

       End Set
     End Property
Klasy i obiekty                                                                     75


Należy zauważyć, że parametr Value umożliwia dostęp do wartości, która ma zostać
wprowadzona. W poniższym fragmencie kodu:
    Dim cp As New CPerson()
    cp.Age = 20

wartość 20 zostaje przekazana do metody Set właściwości Age jako argument Value.


Składowe obiektowe i współużytkowane
Elementy składowe klasy można podzielić na dwie grupy.

Składowe obiektowe
   Dostępne jedynie poprzez instancję, czyli obiekt danej klasy. Innymi słowy
   — składowe obiektowe „należą” do konkretnego obiektu, a nie do klasy jako całości.

Składowe (statyczne) współużytkowane
   Dostęp do składowych współużytkowanych jest możliwy bez tworzenia obiektu
   danej klasy. Te składowe są współużytkowane między wszystkimi obiektami danej
   klasy. Mówiąc ściślej — są niezależne od jakiegokolwiek obiektu klasy.
   Składowe współużytkowane „należą” do klasy jako całości, a nie do jej
   poszczególnych obiektów (czyli instancji).

Dostęp do składowych obiektowych wymaga podania przed nazwą składowej nazwy
obiektu. Tak jak w poniższym fragmencie kodu:
    Dim APerson As New CPerson()
    APerson.Age = 50

Dostęp do składowych współużytkowanych wymaga podania nazwy klasy. Jako przykład
omówimy klasę String w przestrzeni nazw System biblioteki klas .NET Base posia-
dającą metodę współużytkowaną Compare, która porównuje dwa łańcuchy. Składnia
metody Compare:

    Public Shared Function Compare(String, String) As Integer

Metoda Compare w przypadku równych łańcuchów zwraca 0. W przeciwnym razie
zwraca wartość –1, jeżeli pierwszy łańcuch jest mniejszy od drugiego (jest wcześniejszy
w porządku alfabetycznym) i 1, gdy pierwszy łańcuch jest większy od drugiego. Ponieważ
Compare jest metodą współużytkowaną możliwy jest zapis:
    Dim s As String = "steve"
    Dim t As String = "donna"
    MsgBox(String.Compare(s, t))                  ' Wyświetla   1

Należy zauważyć, że nazwa metody Compare poprzedzona jest nazwą klasy String.

Składowe współużytkowane są pomocne podczas kontroli danych niezależnych od
wszystkich obiektów danej klasy. Załóżmy, że chcemy kontrolować liczbę istniejących
obiektów klasy CPerson. Napiszmy następujący podprogram:
76                              Rozdział 3. Wprowadzenie do programowania obiektowego


     ' Deklaracja zmiennej współu ytkowanej przechowującej liczbę obiektów klasy
     Private Shared miInstanceCount As Integer

     ' Zwiększenie wartości miInstanceCount w konstruktorze
     ' (Poni szy kod nale y dodać równie do pozostałych
     ' konstruktorów, o ile takowe zostały zadeklarowane)
     Sub New()
       miInstanceCount += 1
     End Sub

     ' Utworzenie funkcji zwracającej liczbę obiektów
     Shared Function GetInstanceCount() As Integer
       Return miInstanceCount
     End Function

     ' Zmniejszenie wartości miInstanceCount w destruktorze
     Overrides Protected Sub Finalize()
       miInstanceCount -= 1
       MyBase.Finalize
     End Sub

Teraz za pomocą poniższego kodu mamy dostęp do zmiennej współużytkowanej:

     Dim steve As New CPerson()
     MsgBox(CPerson.GetInstanceCount)         ' Wyświetla 1
     Dim donna As New CPerson()
     MsgBox(CPerson.GetInstanceCount)         ' Wyświetla 2


Konstruktory klas
Podczas tworzenia obiektu danej klasy kompilator wywołuje specjalną funkcję zwaną
konstruktorem klasy lub konstruktorem obiektu. Konstruktory mogą służyć do inicjowania
obiektu, gdy zachodzi taka potrzeba (zastępują one zdarzenie Class_Initialize sto-
sowane we wcześniejszych wersjach VB).

Konstruktory można definiować w module klasy. Jeżeli nie zdefiniujemy żadnego
konstruktora, to VB użyje konstruktora domyślnego. W poniższym fragmencie kodu:

     Dim APerson As CPerson = New CPerson()

wywoływany jest domyślny konstruktor klasy CPerson, ponieważ nie zadeklarowaliśmy
własnego konstruktora.

Utworzenie własnego konstruktora polega na zdefiniowaniu procedury o nazwie New
wewnątrz modułu klasy. Przypuśćmy, że chcemy podczas tworzenia obiektu klasy
CPerson nadać jego właściwości Name konkretną wartość. Wtedy powinniśmy umieścić
następujący kod w klasie CPerson:

     ' Konstruktor u ytkownika
     Sub New(ByVal sName As String)
       Me.Name = sName
     End Sub

Teraz można utworzyć obiekt klasy CPerson i ustawić jego nazwę:
Klasy i obiekty                                                                       77

       Dim APerson As CPerson = New CPerson("fred")

lub:
       Dim APerson As New CPerson("fred")

Należy zauważyć, że VB .NET umożliwia przeciążanie (to zagadnienie omówiono w dalszej
części tego rozdziału), więc możliwe jest zdefiniowanie wielu konstruktorów w tej samej
klasie pod warunkiem, że każdy z nich ma unikatową sygnaturę argumentów. W takim
przypadku można wywołać dowolny z nich, podając odpowiednią liczbę oraz odpowiedni
dla tego konstruktora typ argumentów.

Po zadeklarowaniu jednego lub większej ilości konstruktorów użytkownika nie jest
możliwe wywołanie konstruktora domyślnego (tzn. bez parametrów) za pomocą nastę-
pującej instrukcji:
       Dim APerson As New CPerson()

W tym przypadku należy jawnie zadeklarować bezparametrowy konstruktor w module
klasy:
       ' Konstruktor domyślny
       Sub New()
               …
       End Sub


Finalize, Dispose i oczyszczanie pamięci
W VB 6 programista ma możliwość zaimplementowania zdarzenia Class_Terminate
w celu wykonania określonych czynności przed zniszczeniem obiektu. Jeżeli na przykład
obiekt otworzył plik i przechowuje jego uchwyt, ważne jest, by zamknąć ten plik przed
jego zniszczeniem.

W VB .NET nie istnieje zdarzenie Class_Terminate przez co inny jest mechanizm obsługi
tego typu sytuacji. W celu zrozumienia omawianych zagadnień należy najpierw omówić
proces oczyszczania pamięci, zwany inaczej „odśmiecaniem”.

Kiedy program odpowiedzialny za oczyszczanie pamięci stwierdzi, że obiekt nie jest już
potrzebny (ma to na przykład miejsce, gdy wykonywany program nie zawiera już refe-
rencji do tego obiektu), wtedy automatycznie wywoływana jest specjalna metoda destruktora
pod nazwą Finalize. Ważne jest jednak, by zrozumieć, że w przeciwieństwie do zda-
rzenia Class_Terminate nie ma teraz możliwości określenia dokładnego czasu wy-
wołania przez program oczyszczający pamięć metody Finalize. Możemy być jedynie
pewni, że zostanie ona wywołana jakiś czas po zwolnieniu ostatniego odwołania do
obiektu. Opóźnienie wynika z faktu, że .NET Framework używa systemu pod nazwą
odśmiecanie ze śledzeniem odwołań, który zwalnia okresowo nieużywane zasoby.

Metoda Finalize jest metodą Protected. Oznacza to, że może być wywoływana je-
dynie z klasy bazowej oraz jej klas pochodnych. Nie ma możliwości wywołania metody
Finalize spoza klasy (klasa nie powinna praktycznie nigdy wywoływać swojej metody
78                                Rozdział 3. Wprowadzenie do programowania obiektowego


Finalize bezpośrednio, ponieważ destruktor Finalize wywoływany jest automa-
tycznie przez program oczyszczający pamięć). Jeżeli utworzymy metodę Finalize danej
klasy, to powinniśmy również jawnie w jej treści wywołać metodę Finalize jej klasy
bazowej. W ten sposób składnia i format metody Finalize wygląda następująco:

     Overrides Protected Sub Finalize()
       ' Zaplanowane czynności
       MyBase.Finalize
     End Sub

Korzyści wynikające z tego typu oczyszczania pamięci polegają na automatyzacji tego
procesu oraz zapewnieniu tego, że niewykorzystane zasoby zostaną na pewno uwolnio-
ne bez jakiejkolwiek ingerencji ze strony programisty. Natomiast wadą jest brak możli-
wości bezpośredniego zainicjowania oczyszczania pamięci przez aplikację, przez co nie-
które zasoby mogą pozostać w użyciu dłużej, niż to jest konieczne. Mówiąc prosto z mostu:
nie możemy zniszczyć obiektu na żądanie.

Należy wziąć pod uwagę, że nie wszystkie zasoby są zarządzane przez Common Lan-
guage Runtime. Niektóre zasoby, takie jak na przykład uchwyty okien i połączenia z bazami
danych, nie podlegają automatycznemu oczyszczaniu. Dlatego w celu ich zwolnienia
należy dołączyć odpowiedni kod w metodzie Finalize. Ten sposób nie umożliwia
jednak zwalniania zasobów na żądanie. Do tego celu służy drugi destruktor o nazwie
Dispose, który jest zdefiniowany w bibliotece klas Base. Destruktor Dispose ma nastę-
pującą składnię:
     Class classname
       Implements IDisposable
       Public Sub Dispose() Implements IDisposable.Dispose
         ' Zaplanowane czynności (np. zwalnianie zasobów)
         ' Wywołanie metod Dispose obiektów potomnych, je eli to jest konieczne
       End Sub

       ' Pozostałe składowe klasy
     End Class

Należy zauważyć, że klasy wykorzystujące ten rodzaj destruktora muszą implementować
interfejs IDisposable — z tego powodu konieczna jest instrukcja Implements pokazana
powyżej. Interfejs IDisposable ma tylko jedną składową, a jest nią metoda Dispose.

Konieczne jest poinformowanie użytkowników klasy, że muszą oni wywoływać tę
metodę jawnie w celu zwolnienia zasobów (technicznym terminem jest tutaj określenie
ręczne podejście!).


Dziedziczenie
Najlepszym sposobem opisania mechanizmu dziedziczenia zastosowanego w VB .NET
będzie podanie następującego przykładu.

Klasy wykorzystywane przez aplikację często są powiązane między sobą. Weźmy jako
przykład dane odnoszące się do pracowników. Wspólne dane wszystkich pracowników
Dziedziczenie                                                                     79


reprezentowane są przez obiekty Employee (pracownik) klasy CEmployee — można
tutaj wymienić imię, nazwisko, nazwisko, adres, pobory itd.

Dodatkowe zarobki kierownictwa przedsiębiorstwa będą oczywiście inne niż analogiczne
zarobki, powiedzmy, osoby pracującej na stanowisku sekretarki. Z tego względu rozsąd-
nie byłoby zdefiniować dodatkowe klasy CExecutive i CSecretary, każdą ze swoimi
własnymi właściwościami i metodami. Z drugiej strony — kierownik, to także pracow-
nik i nie ma powodu definiowania dwu różnych właściwości Name w tym przypadku.
Takie postępowanie jest nieefektywne i prowadzi do marnowania zasobów.

W tym właśnie celu stosuje się dziedziczenie. Najpierw zdefiniujemy klasę CEmployee,
która implementuje właściwość Salary i metodę IncSalary:

    ' Klasa Employee
    Public Class CEmployee
      ' Właściwość Salary umo liwia zapis / odczyt
      Private mdecSalary() As Decimal
      Property Salary() As Decimal
        Get
          Salary = mdecSalary
        End Get
        Set(ByVal Value as Decimal)
          mdecSalary = Value
        End Set
      End Property
      Public Overridable Sub IncSalary(ByVal sngPercent As Single)
        mdecSalary = mdecSalary * (1 + CDec(sngPercent))
      End Sub
    End Class

Następnie definiujemy klasę CExecutive:

    ' Klasa CExecutive
    Public Class CExecutive
      Inherits CEmployee
      ' Oblicz wzrost zarobków uwzgledniąjacy 5% dodatku na samochód
      Overrides Sub IncSalary(ByVal sngPercent As Single)
        Me.Salary = Me.Salary * CDec(1.05 + sngPercent)
      End Sub
    End Class

Należy zwrócić uwagę na dwie kwestie. Po pierwsze instrukcja:

    Inherits CEmployee

określa, że klasa CExecutive dziedziczy składowe klasy CEmployee. Mówiąc inaczej
— obiekt klasy CExecutive jest również obiektem klasy CEmployee. Zatem jeżeli
utworzymy obiekt klasy CExecutive:

    Dim ceo As New CExecutive

to mamy dostęp do właściwości Salary:

    ceo.Salary = 1000000
80                                  Rozdział 3. Wprowadzenie do programowania obiektowego


Po drugie — słowo kluczowe Overrides w metodzie IncSalary oznacza, że metoda
IncSalary klasy CExecutive jest wywoływana zamiast metody zaimplementowanej
w CEmployee. Zatem kod:
      ceo.IncSalary

zwiększa pobory obiektu ceo klasy CExecutive uzględniając dodatek na samochód.
W deklaracji metody IncSalary klasy CEmployee znajduje się słowo kluczowe
Overridable oznaczające, że klasy dziedziczące od klasy podstawowej mogą prze-
słaniać odnośną metodę klasy bazowej.

Następnie definiujemy nową klasę CSecretary, która też dziedziczy z klasy CEmployee,
ale implementuje inny rodzaj dodatku do pensji:
      ' Klasa CSecretary
      Public Class CSecretary
        Inherits CEmployee
        ' Sekretarka otrzymuje 2% dodatek za nadgodziny
        Overrides Sub IncSalary(ByVal sngPercent As Single)
          Me.Salary = Me.Salary * CDec(1.02 + sngPercent)
        End Sub
      End Class

Napiszemy teraz kod wykorzystujący powyższe klasy:
      ' Utworzenie nowych obiektów
      Dim ThePresident As New CExecutive()
      Dim MySecretary As New CSecretary()
      ' Ustalenie zarobków
      ThePresident.Salary = 1000000
      MySecretary.Salary = 30000
      ' Wyświetl zarobki przewodniczącego i sekretarki bez oraz z dodatkiem
      Debug.WriteLine("Prezes przed: " & CStr(ThePresident.Salary))
      ThePresident.IncSalary(0.4)
      Debug.WriteLine("Prezes po: " & CStr(ThePresident.Salary))

      Debug.WriteLine("Sekretarka przed: " & CStr(MySecretary.Salary))
      MySecretary.IncSalary(0.4)
      Debug.WriteLine("Sekretarka po: " & CStr(MySecretary.Salary))

Wynik wykonania programu:
      Prezes przed: 1000000
      Prezes po: 1450000
      Sekretarka przed: 30000
      Sekretarka po: 42600

Dziedziczenie jest stosunkowo prostym pojęciem. W dokumentacji Microsoftu znajduje
się taki jego opis:

     Jeżeli Klasa B dziedziczy od Klasy A, to każdy obiekt Klasy B jest również obiektem
     Klasy A i zawiera publiczne metody i właściwości (tzn. interfejs publiczny) Klasy A.
     W tym przypadku Klasa A nazywana jest klasą bazową, a Klasa B klasą potomną.
     Klasa potomna może przesłaniać składowe klasy podstawowej zgodnie ze swoimi potrzebami.

W poprzednim przykładzie zauważyliśmy, że słowem kluczowym definiującym dziedzi-
czenie jest Inherits.
Dziedziczenie                                                                         81


Pozwolenie na dziedziczenie
Istnieją dwa słowa kluczowe, używane w deklaracji klasy bazowej, które określają moż-
liwość dziedziczenia z tej klasy.

NotInheritable
  Użycie tego słowa kluczowego w deklaracji klasy:
    Public NotInheritable Class InterfaceExample

   powoduje, że klasa nie może być klasą bazową.

MustInherit
  Użycie tego słowa kluczowego w deklaracji klasy:
    Public MustInherit Class InterfaceExample

   sprawia, że obiekty tej klasy nie mogą być tworzone bezpośrednio. Mogą być
   natomiast tworzone obiekty klas potomnych. Innymi słowy — klasy MustInherit
   mogą być klasami bazowymi i tylko klasami bazowymi.


Przesłanianie
Istnieje kilka słów kluczowych określających to, czy (i w jaki sposób) klasa potomna może
przesłaniać implementację klasy bazowej. Omawiane słowa kluczowe używane są w de-
klaracji odnośnej składowej, a nie w deklaracji klasy.

Overridable
  Zezwala na przesłanianie, jednak nie wymaga, by składowa była przesłaniana.
  Domyślnym ustawieniem dla składowej Public jest NotOverridable:
    Public Overridable Sub IncSalary()

NotOverridable
  Zabrania przesłaniania danej składowej. Domyślne ustawienie dla składowych
  Public klasy.

MustOverride
  Składowa musi zostać przesłonięta. W przypadku użycia tego słowa kluczowego
  nie podajemy w deklaracji składowej implementacji i nie używamy słów kluczowych
  End Sub i End Function:
    Public MustOverride Sub IncSalary()

   Jeżeli klasa zawiera składową ze słowem kluczowym MustOverride, to musi zostać
   zadeklarowana jako klasa MustInherit.

Overrides
  W przeciwieństwie do poprzednich modyfikatorów ten modyfikator stosowany jest
  w przypadku składowych klas potomnych i wskazuje, że modyfikowana składowa
  przesłania składową klasy bazowej:
    Overrides Sub IncSalary()
82                               Rozdział 3. Wprowadzenie do programowania obiektowego


Reguły dziedziczenia
W wielu językach obiektowych, takich jak na przykład w C++, klasa może dziedziczyć
bezpośrednio z więcej niż jednej klasy bazowej. Określane jest to dziedziczeniem wielo-
krotnym. W VB .NET nie jest możliwe dziedziczenie wielokrotne. W ten sposób klasa
może bezpośrednio dziedziczyć najwyżej z jednej klasy. Poniższy fragment kodu nie jest
więc poprawny:

     ' Klasa Executive
     Public Class CExecutive                 ' NIEPOPRAWNE
       Inherits CEmployee
       Inherits CWorker
       …
     End Class

Natomiast Klasa C może dziedziczyć z Klasy B, która z kolei może dziedziczyć z Klasy
A. W ten sposób możliwe jest utworzenie hierarchii dziedziczenia. Klasa może również
implementować wiele interfejsów za pomocą słowa kluczowego Interface. Powyższe
zagadnienie jest omawiane w dalszej części tego rozdziału.


MyBase, MyClass i Me
Słowo kluczowe MyBase umożliwia dostęp do klasy bazowej z klasy potomnej. W celu
wywołania składowej klasy bazowej z klasy potomnej należy użyć następującej składni:

     MyBase.MemberName

gdzie w miejscu MemberName podaje się nazwę składowej klasy, do której mamy się
odwołać. W ten sposób nie powstaje żadna dwuznaczność w przypadku, gdy klasa po-
tomna ma również składową o tej samej nazwie.

Słowo kluczowe MyBase może zostać użyte do wywołania konstruktora klasy bazowej
przy tworzeniu obiektu klasy potomnej:
     MyBase.New(…)

Nie można za pomocą MyBase wywoływać składowych klasy o dostępie Private.

W przypadku użycia słowa kluczowego MyBase wyszukiwana jest składowa znajdująca
się najbliżej w drzewie dziedziczenia. Zatem jeżeli klasa C dziedziczy od klasy B, która
dziedziczy od klasy A, to wywołanie procedury AProc w klasie C:

     MyBase.AProc

powoduje, że najpierw przeszukiwana jest klasa B w celu znalezienia procedury o nazwie
AProc. Jeżeli w tej klasie nie zostanie znaleziona poszukiwana procedura, to VB .NET
przeszuka klasę A, szukając odpowiedniej procedury (pod pojęciem odpowiedniej proce-
dury rozumiemy procedurę o takiej samej nazwie i sygnaturze argumentów).
Dziedziczenie                                                                    83


Słowo kluczowe MyClass umożliwia dostęp do klasy, w której zostało użyte. MyClass
jest podobne do słowa kluczowego Me. Jedyna różnica występuje przy wywoływaniu
metod. W celu zilustrowania tego zagadnienia utworzymy klasę Class1 i klasę pochodną
o nazwie Class1Derived. Obie klasy posiadają metodę IncSalary:
    Public Class Class1
      Public Overridable Function IncSalary(ByVal sSalary As Single) _
                                            As Single
        IncSalary = sSalary * CSng(1.1)
      End Function
      Public Sub ShowSalary(ByVal sSalary As Single)
        MsgBox(Me.IncSalary(sSalary))
        MsgBox(MyClass.IncSalary(sSalary))
      End Sub
    End Class

    Public Class Class1Derived
      Inherits Class1
      Public Overrides Function IncSalary(ByVal sSalary As Single) _
                                          As Single
        IncSalary = sSalary * CSng(1.2)
      End Function
    End Class

Przeanalizujmy teraz poniższy fragment kodu:
    Dim c1 As New Class1()
    Dim c2 As New Class1Derived()

    Dim clvar As Class1

    clvar = c1
    clvar.ShowSalary(10000)                ' Wyświetla 11000, 11000

    clvar = c2
    clvar.ShowSalary(10000)                ' Wyświetla 12000, 11000

Pierwsze wywołanie metody IncSalary wykorzystuje zmienną typu Class1, która
jest referencją do obiektu typu Class1. W tym przypadku oba poniższe wywołania:
    Me.IncSalary
    MyClass.IncSalary

zwracają taką samą wartość, ponieważ wywołują metodę IncSalary w klasie bazowej
Class1.

Jednak za drugim razem zmienna typu Class1 zawiera referencję do obiektu klasy
potomnej — Class1Derived. Teraz Me odnosi się do obiektu typu Class1Derived,
podczas gdy MyClass nadal wskazuje klasę bazową Class1, w której słowo kluczowe
MyClass występuje. W ten sposób:
    Me.IncSalary

zwraca 12000, natomiast:
    MyClass.IncSalary

zwraca 11000.
84                               Rozdział 3. Wprowadzenie do programowania obiektowego


Interfejsy, abstrakcyjne składowe i klasy
W trakcie wcześniejszych rozważań wspomnieliśmy już, że klasa może implementować
wszystkie składowe interfejsu, część lub może nie implementować żadnej składowej inter-
fejsów, które deklaruje. Składowa interfejsu, która nie zawiera swojej implementacji
określana jest jako składowa abstrakcyjna. Zadaniem składowej abstrakcyjnej jest podanie
sygnatury składowej (wzorca), która może zostać zaimplementowana przez jedną lub więcej
klas potomnych w różny sposób w każdej z nich.

Wyjaśnimy powyższe zagadnienie na przykładzie. Przypomnijmy, że klasa CEmployee
deklaruje i implementuje metodę IncSalary zwiększającą pobory pracownika. Ponadto
dodajmy, że klasy potomne CExecutive i CSecretary przesłaniają implementację
metody IncSalary klasy bazowej CEmployee.

Przypuśćmy, że utworzyliśmy bardziej rozbudowany model pracowników, w którym
każdemu stanowisku przyporządkowano oddzielną klasę pochodną. Każda z tych klas
przesłania implementację metody IncSalary klasy bazowej CEmployee. W tym przy-
padku implementacja metody IncSalary klasy bazowej nie musi być nigdy wywoły-
wana. Po co więc podawać implementację metody, która nigdy nie będzie wykorzystana?

Zamiast tego, możemy po prostu utworzyć metodę IncSalary z pustym „ciałem”:
     ' Klasa Pracownik
     Public Class CEmployee
       …
       Public Overridable Sub IncSalary(ByVal sngPercent As Single)
       End Sub
     End Sub

Jeżeli wszystkie klasy pochodne muszą implementować metodę IncSalary, wtedy należy
użyć słowa kluczowego MustOverride:

     ' Klasa pracownik
     Public MustInherit Class CEmployee
       …
       Public MustOverride Sub IncSalary(ByVal sngPercent As Single)

     End Class

Jak już stwierdziliśmy, nie ma skojarzonej instrukcji End Sub ze słowem kluczo-
wym MustOverride. Należy również pamiętać, że słowo kluczowe MustOverride
wymaga, by klasa, w której zostało użyte, zadeklarowana została ze słowem klu-
czowego MustInherit. W ten sposób stwierdzamy, że nie wolno bezpośrednio
tworzyć obiektów klasy CEmployee.

W powyższych przykładach składowa IncSalary klasy bazowej CEmployee jest skła-
dową abstrakcyjną.

Klasa z przynajmniej jedną składową abstrakcyjną nazywana jest klasą abstrakcyjną. Zatem
zdefiniowana powyżej klasa CEmployee jest klasą abstrakcyjną. Zastosowana termino-
Interfejsy, abstrakcyjne składowe i klasy                                              85


logia wynika z faktu, że nie jest możliwe utworzenie obiektu klasy abstrakcyjnej, ponieważ
przynajmniej jedna z metod obiektu nie posiadałaby implementacji.

W pewnych sytuacjach może nam być potrzebna klasa, której wszystkie składowe są
abstrakcyjne. Innymi słowy — jest to klasa, która jedynie definiuje interfejs. Taką klasę
można określić mianem klasy czysto abstrakcyjnej, chociaż nie jest to ogólnie przyjęte
sformułowanie.

Jako przykład weźmy klasę określającą kształt zwaną CShape, której celem jest przed-
stawienie ogólnych właściwości i operacji, które mogą być wykonywane na figurach
geometrycznych (elipsach, prostokątach, trapezoidach itp.). Każdy kształt czy figura
wymaga metody Draw do narysowania samej siebie. Implementacja takiej metody bę-
dzie różna dla różnych rodzajów figur — na przykład inaczej rysujemy okręgi, a inaczej
prostokąty. W identyczny sposób będziemy musieli jednak dołączyć metody, takie jak
Rotate (obrót), Translate (przesunięcie) i Reflect (odbicie). Każda z tych metod
będzie posiadała inną implementację (w zależności od reprezentowanej figury czy kształtu).

Zatem możemy zdefiniować klasę CShape na dwa sposoby:

       Public Class Class2

         Public Overridable Sub Draw()
         End Sub

         Public Overridabe Sub Rotate(ByVal sngDegrees As Single)
         End Sub

         Public Overridabe Sub Translate(ByVal x As Integer, _
                                         ByVal y As Integer)
         End Sub

         Public Overridable Sub Reflect(ByVal iSlope As Integer, _
                                        ByVal iIntercept As Integer)
         End Sub
       End Class

lub:

       Public MustInherit Class CShape

         Public MustOverride Sub Draw()
         Public MustOverride Sub Rotate(ByVal sngDegresss As Single)
         Public MustOverride Sub Rotate(ByVal x As Integer, _
                                        ByVal y As Integer)
         Public MustOverride Sub Reflect(ByVal iSlope As Integer, _
                                         ByVal iIntercept As Integer)
       End Class

Teraz możemy zdefiniować klasy potomne, takie jak CRectangle, CEllipse, CPoly-
gon itp. Każda z tych klas będzie implementować (lub musi implementować w drugim
przypadku) składowe klasy bazowej CShape. Nie omawiamy tutaj jednak szczegółów
takiej implementacji, gdyż nie stanowi to głównego tematu naszych rozważań.
86                               Rozdział 3. Wprowadzenie do programowania obiektowego


Interfejsy raz jeszcze
Powiedzieliśmy, że interfejsy mogą być zdefiniowane w modułach klas. W VB .NET moż-
liwy jest również inny sposób definiowania interfejsu za pomocą słowa kluczowego
Interface. Poniżej zdefiniowano interfejs IShape:
     Public Interface IShape
       Sub Draw()
       Sub Rotate(ByVal sngDegrees As Single)
       Sub Translate(ByVal x As Integer, ByVal y As Integer)
       Sub Reflect(ByVal iSlope As Integer, _
                   ByVal iIntercept As Integer)
     End Interface

W przypadku zastosowania słowa kluczowego Interface nie można implementować
składowych wewnątrz modułu, w którym zdefiniowano interfejs. Można natomiast
zaimplementować interfejs za pomocą zwykłego modułu klasy. Ważne jest wykorzysta-
nie w tej sytuacji instrukcji Implements (która była dostępna również w VB 6, jednak
mogła odnosić się jedynie do interfejsów zewnętrznych):
     Public Class CRectangle

     ' Implementacja interfejsu IShape
       Implements IShape

       Public Overridable Sub Draw() Implements IShape.Draw
         ' kod metody
       End Sub

       Public Overridable Sub Spin(ByVal sngDegrees As Single) Implements
     IShape.Rotate
         ' kod metody
       End Sub

       ...

     End Class

Należy zauważyć, że słowo kluczowe Implements występuje dodatkowo w każdej
funkcji, która implementuje składową interfejsu. Zastosowanie tego słowa kluczowego
umożliwia nadanie funkcji implementującej dowolnej nazwy. Funkcja implementująca
nie musi mieć takiej samej nazwy jak metoda (metoda Spin implementuje metodę Rotate
interfejsu IShape). Jednak nadawanie tej samej nazwy obu funkcjom jest niewątpliwie
bardziej czytelne (a przez to prezentuje lepszy styl programowania.)

Główną korzyścią wynikającą ze stosowania słowa kluczowego Implements do defi-
niowania interfejsu jest fakt, że pojedyncza klasa może w ten sposób implementować
wiele interfejsów. Dzieje się tak, pomimo że VB .NET nie zezwala na dziedziczenie w ob-
rębie jednej klasy z wielu klas bazowych. Z drugiej strony — stosowanie słowa kluczo-
wego Interface wiąże się z brakiem możliwości podania implementacji w module
definiującym ten interfejs. W ten sposób wszystkie składowe muszą zostać zaimplemento-
wane w każdej klasie implementującej dany interfejs. Oznacza to powtarzanie tego samego
kodu w przypadku, gdy składowa interfejsu ma taką samą implementację w więcej niż
jednej implementującej klasie.
Polimorfizm i przeciążanie                                                            87


Polimorfizm i przeciążanie
Na szczęście nie musimy wnikać w szczegóły leżące u podstaw mechanizmu polimorfizmu
i przeciążania z całą ich zawiłością i dwuznacznością. Niektórzy informatycy na przy-
kład mówią, że przeciążanie jest formą polimorfizmu, inni natomiast twierdzą, że nie jest
tak. Omówimy jedynie te zagadnienia, które wiążą się bezpośrednio z .NET Framework.


Przeciążanie
Terminu przeciążanie używamy w odniesieniu do takich elementów języka, które mogą
zostać użyte w więcej niż jeden sposób. Nazwy operatorów są często przeciążane. Znak
plus (+) na przykład może określać dodawanie liczb całkowitych, dodawanie liczb typu
Single, dodawanie liczb Double czy łączenie łańcuchów. W ten sposób symbol plus
(+) jest przeciążany. Ma to swoje dobre strony, gdyż w przeciwnym przypadku musieli-
byśmy używać oddzielnych symboli do dodawania liczb całkowitych, liczb typu Single
czy typu Double.

Nazwy funkcji są również przeciążane. Funkcja Abs zwracająca wartość bezwzględną
liczby może pobierać jako parametr liczbę całkowitą, liczbę typu Single lub typu
Double. Ponieważ nazwa Abs reprezentuje klika różnych funkcji, to znaczy to, że jest
nazwą przeciążoną. Rzeczywiście tak jest — przeglądając dokumentację składowej Abs
klasy Math (w systemowej przestrzeni nazw biblioteki klas Base) znajdujemy następu-
jące deklaracje funkcji o nazwie Abs:

    Overloads   Public   Shared   Function   Abs(Decimal) As Decimal
    Overloads   Public   Shared   Function   Abs(Double) As Double
    Overloads   Public   Shared   Function   Abs(Short) As Short
    Overloads   Public   Shared   Function   Abs(Integer) As Integer
    Overloads   Public   Shared   Function   Abs(Long) As Long
    Overloads   Public   Shared   Function   Abs(SByte) As SByte
    Overloads   Public   Shared   Function   Abs(Single) As Single

Słowo kluczowe Overloads oznacza, że funkcja jest przeciążona.

Nazwa funkcji jest przeciążona, jeżeli dwie zdefiniowane funkcje mają tę samą nazwę,
ale różne sygnatury argumentów. Weźmy jako przykład funkcję zwracającą bieżące saldo
rachunku. Rachunek może być identyfikowany przez nazwę osoby albo przez numer
rachunku. W ten sposób możemy zdefiniować dwie funkcje i obydwie nazwać GetBalance:

    Overloads Function GetBalance(sCustName As String) As Decimal
    Overloads Function GetBalance(sAccountNumber As Long) As Decimal

Należy zauważyć, że przeciążanie funkcji w VB .NET możliwe jest dlatego, że sygnatury ar-
gumentów dwu funkcji są różne. W ten sposób nie ma dwuznaczności. Wywołania funkcji:

    getBalance("John Smith")
    getBalance(123456)
88                               Rozdział 3. Wprowadzenie do programowania obiektowego


są interpretowane przez kompilator bez trudności na podstawie typu danych argumentu.
Ten rodzaj przeciążania jest często określany mianem przeciążania funkcji GetBalance.
Przedstawione powyżej funkcje są jednak dwiema różnymi funkcjami, bardziej poprawne
jest więc stwierdzenie, że przeciążana jest nazwa funkcji. Przeciążanie jest powszechnie
stosowaną techniką programistyczną, której zastosowanie wykracza daleko poza pro-
gramowanie obiektowe.


Polimorfizm
Termin polimorfizm oznacza występowanie w wielu różnych postaciach. W .NET Fra-
mework polimorfizm jest ściśle związany z dziedziczeniem. Ponownie wróćmy do przy-
kładu klasy opisującej pracownika przedsiębiorstwa. Funkcja IncSalary została zdefi-
niowana w trzech klasach: klasie bazowej CEmployee oraz jej klasach potomnych
CExecutive i CSecretary. W ten sposób funkcja IncSalary występuje w trzech posta-
ciach. To jest właśnie polimorfizm w stylu VB .NET.

Osoby zainteresowane omawianym zagadnieniem należy poinformować, że wielu infor-
matyków nie uznaje przedstawionego powyżej mechanizmu za polimorfizm. W tym miej-
scu powiedzieliby, że funkcja IncSalary przyjmuje tylko jedną postać. Różne są jedynie
implementacje, a nie funkcja. Opisaną sytuację określiliby mianem przeciążenia funkcji
IncSalary. Problemem występującym w tym przypadku jest zamieszanie w sposobie, w jaki
Microsoft oraz pozostałe osoby używają terminów przeciążenie i polimorfizm — dlatego
należy być ostrożnym podczas czytania dokumentacji.


Zasięg i dostęp w module klasy
Pojęcie zasięgu w module klasy jest znacznie ważniejsze niż w zwykłych modułach.
Nie ma większych różnic w przypadku rozpatrywania zmiennych lokalnych (o zasięgu
ograniczonym do bloku lub procedury) — tak samo w module klasy mamy zmienne
o zasięgu ograniczonym do bloku oraz zmienne ograniczone do procedury.

Natomiast zmiennym zadeklarowanym części Declarations modułu klasy może być
przypisany jeden z następujących modyfikatorów dostępu:
• Public;
• Private;
• Friend;
• Protected;
• Protected Friend.

W przypadku zwykłych modułów dozwolone są jedynie Public, Private i Friend.
Zasięg i dostęp w module klasy                                                      89


Sam moduł klasy może być zadeklarowany z jednym z trzech modyfikatorów dostępu:
Public, Private lub Friend (Protected nie jest dozwolony). Podanie w deklaracji
modułu klasy jednego z tych modyfikatorów dostępu po prostu ogranicza dostęp do
wszystkich składowych modułu do tego poziomu dostępu. Podany zasięg może zostać
dalej ograniczony dla konkretnej składowej poprzez umieszczenie modyfikatora dostępu
w deklaracji tej składowej. W ten sposób w przypadku dostępu do klasy typu Friend
żadna z jej składowych nie może mieć dostępu typu Public (innymi słowy — dostęp
typu Public zostaje przesłonięty dostępem typu Friend).

Natomiast wszystkie cztery modyfikatory dostępu mają wpływ na składowe modułu
klasy — to znaczy na deklaracje zmiennych, stałych, wyliczeń oraz procedur wewnątrz
modułu klasy.

Problem wynika z powodu istnienia trzech typów dostępu do składowej klasy o różnym
zasięgu. W celu wyjaśnienia zdefiniujmy następujące składowe — przedstawione deklaracje
nie są być może standardowe, ale z pewnością dobrze opisują zagadnienie. Przeanalizujmy
następującą deklarację zmiennej w części Declarations modułu klas o nazwie Class1:
    AccessModifier classvariable As Integer

Dostęp do tej zmiennej jest możliwy w przedstawiony niżej sposób.

Bezpośredni dostęp
   Określa dostęp do składowej bez żadnych dodatkowych określeń:
    classvariable = 100

   Podczas dostępu do zmiennej w bezpośredni sposób (tzn. bez dodatkowych
   określeń), zasięg zmiennej może obejmować:
   • klasę deklarującą;
   • klasę deklarującą i jej klasy pochodne (jedynie wewnątrz projektu, w którym
     ma miejsce deklaracja);
   • klasę deklarującą i jej klasy pochodne (w każdym projekcie zawierającym
     odwołanie do projektu, w którym ma miejsce deklaracja).

Dostęp klasowy/obiektowy
  Określa dostęp do składowej za pomocą określenia w postaci nazwy klasy lub nazwy
  obiektu tej klasy.
   Stwierdziliśmy już, że w przypadku zadeklarowania zmiennej za pomocą słowa
   kluczowego Shared, jest ona wspólna dla wszystkich obiektów klasy. Wyrażając się
   ściślej można powiedzieć, że taka składowa istnieje niezależnie od jakiegokolwiek
   obiektu klasy. W tym przypadku dostęp do składowej (wewnątrz zasięgu składowej)
   jest możliwy przez podanie określenia w postaci nazwy klasy:
    Class1.classvariable = 100

   Należy zauważyć, że dostęp do takiej składowej jest możliwy również poprzez
   podanie nazwy obiektu. Rezultat jest taki sam jak w przypadku podania nazwy klasy
   — istnieje jedynie jedna kopia składowej.
90                                    Rozdział 3. Wprowadzenie do programowania obiektowego


     Jeżeli składowa jest zadeklarowana bez użycia słowa Shared, wtedy dostęp możliwy
     jest przez podanie nazwy istniejącego obiektu:
      Dim c As new Class1
      c.classvariable = 100

     Zasięg klasowy/obiektowy może obejmować:
     • deklarującą klasę;
     • deklarujący projekt;
     • deklarujący projekt i te komponenty zewnętrzne, które mają odwołanie
       do deklarującego projektu.

Tabela 3.2 opisuje różne modyfikatory dostępu.

Tabela 3.2. Modyfikatory dostępu w module klasy

                            Zasięg przy dostępie bezpośrednim     Zasięg klasy/obiektu
 Private                    Klasa deklarująca                     Klasa deklarująca
 Protected                  Wszystkie pochodne klasy              Klasa deklarująca
 Friend                     Klasy pochodne w projekcie            Deklarowany projekt
 Protected Friend           Wszystkie pochodne klasy              Deklarowany projekt
 Public                     Wszystkie pochodne klasy              Wszystkie projekty

Nie było możliwe niezależne przedstawienie wpływu modyfikatorów Friend i Protec-
ted. Lepszym rozwiązaniem byłoby zapewne oddzielne przedstawienie modyfikatorów
dostępu przy bezpośrednim i klasowym lub obiektowym dostępie, zamiast powyższego
przeplatania pojęć w tabeli 3.2. No cóż...

More Related Content

PDF
Tworzenie stron WWW w praktyce
PDF
JavaScript i DHTML. Receptury
PDF
Tworzenie stron WWW. Kurs
PDF
Programowanie obiektowe w Visual Basic .NET dla każdego
PDF
Tworzenie stron WWW w praktyce. Wydanie II
PDF
Visual Basic .NET. Encyklopedia
PDF
HTML i XHTML dla każdego
PDF
PHP i MySQL. Tworzenie stron WWW. Wydanie drugie. Vademecum profesjonalisty
Tworzenie stron WWW w praktyce
JavaScript i DHTML. Receptury
Tworzenie stron WWW. Kurs
Programowanie obiektowe w Visual Basic .NET dla każdego
Tworzenie stron WWW w praktyce. Wydanie II
Visual Basic .NET. Encyklopedia
HTML i XHTML dla każdego
PHP i MySQL. Tworzenie stron WWW. Wydanie drugie. Vademecum profesjonalisty

What's hot (20)

PDF
Tworzenie stron WWW. Kurs. Wydanie II
PDF
Dreamweaver MX
PDF
JavaScript dla każdego. Wydanie IV
PDF
Po prostu HTML 4. Wydanie III
PDF
Po prostu PHP. Techniki zaawansowane
PDF
Flash MX. Vademecum profesjonalisty
PDF
HTML w 10 prostych krokach
PDF
Dreamweaver MX. Ćwiczenia
PDF
PHP5. Praktyczny kurs
PDF
Kurs tworzenia stron internetowych
PDF
Macromedia Studio 8. Oficjalny podręcznik
PDF
Java. Usługi WWW. Vademecum profesjonalisty
PDF
Visual Basic 2005. Od podstaw
PDF
PHP i MySQL. Dynamiczne strony WWW. Szybki start. Wydanie II
PDF
Praktyczny kurs Java
PDF
Flash 8. Klatka po klatce
PDF
Tworzenie stron WWW. Nieoficjalny podręcznik
PDF
.NET Framework 2.0. Zaawansowane programowanie
PDF
PHP w mgnieniu oka
PDF
Contribute 2. Szybki start
Tworzenie stron WWW. Kurs. Wydanie II
Dreamweaver MX
JavaScript dla każdego. Wydanie IV
Po prostu HTML 4. Wydanie III
Po prostu PHP. Techniki zaawansowane
Flash MX. Vademecum profesjonalisty
HTML w 10 prostych krokach
Dreamweaver MX. Ćwiczenia
PHP5. Praktyczny kurs
Kurs tworzenia stron internetowych
Macromedia Studio 8. Oficjalny podręcznik
Java. Usługi WWW. Vademecum profesjonalisty
Visual Basic 2005. Od podstaw
PHP i MySQL. Dynamiczne strony WWW. Szybki start. Wydanie II
Praktyczny kurs Java
Flash 8. Klatka po klatce
Tworzenie stron WWW. Nieoficjalny podręcznik
.NET Framework 2.0. Zaawansowane programowanie
PHP w mgnieniu oka
Contribute 2. Szybki start
Ad

Viewers also liked (16)

PDF
ASP.NET. Vademecum profesjonalisty
PDF
Projektowanie baz danych XML. Vademecum profesjonalisty
PDF
Mandriva. Ćwiczenia
PDF
Windows XP PL. Biblia
PDF
Microsoft SQL Server 2000. Księga eksperta
PDF
Oracle. Optymalizacja wydajności
PDF
sendmail. Receptury
PDF
TCP/IP. Księga eksperta
PDF
JavaScript. Projekty
PDF
ABC Delphi 6
PDF
ABC Photoshop 7
PDF
C# 3.0 i .NET 3.5. Technologia LINQ
PDF
JavaScript. Ćwiczenia praktyczne. Wydanie II
PDF
.NET CLR. Księga eksperta
PDF
Joomla! System zarządzania treścią
PDF
Mathcad. Ćwiczenia. Wydanie II
ASP.NET. Vademecum profesjonalisty
Projektowanie baz danych XML. Vademecum profesjonalisty
Mandriva. Ćwiczenia
Windows XP PL. Biblia
Microsoft SQL Server 2000. Księga eksperta
Oracle. Optymalizacja wydajności
sendmail. Receptury
TCP/IP. Księga eksperta
JavaScript. Projekty
ABC Delphi 6
ABC Photoshop 7
C# 3.0 i .NET 3.5. Technologia LINQ
JavaScript. Ćwiczenia praktyczne. Wydanie II
.NET CLR. Księga eksperta
Joomla! System zarządzania treścią
Mathcad. Ćwiczenia. Wydanie II
Ad

Similar to VB .NET. Almanach (20)

PDF
Visual Basic 2005. Wprowadzenie do programowania w .NET
PDF
Visual Basic .NET. Ćwiczenia
PDF
Visual Basic .NET. Księga eksperta
PDF
Microsoft Visual Basic .NET 2003. Księga eksperta
PDF
Visual Basic 2005. Zapiski programisty
PDF
Delphi 2005
PDF
Aplikacje w Visual C++ 2005. Przykłady
PDF
Visual C# 2008. Projektowanie aplikacji. Pierwsze starcie
PDF
Visual Basic 2008. Warsztat programisty
PDF
Visual Basic 2005. Almanach
PDF
Visual Basic .NET. Bazy danych. Księga eksperta
PDF
Visual C# 2005. Zapiski programisty
PDF
Delphi 8 .NET. Kompendium programisty
PDF
Delphi dla .NET. Vademecum profesjonalisty
PDF
Visual C# 2005 Express Edition. Od podstaw
PDF
C#. Leksykon kieszonkowy
PDF
Delphi 7 dla każdego
PDF
Visual Basic 2005 Express i My Sql ebook
PDF
Wstęp do programowania w języku C#
PDF
Visual C++ 2005 Express Edition. Tworzenie aplikacji dla Windows
Visual Basic 2005. Wprowadzenie do programowania w .NET
Visual Basic .NET. Ćwiczenia
Visual Basic .NET. Księga eksperta
Microsoft Visual Basic .NET 2003. Księga eksperta
Visual Basic 2005. Zapiski programisty
Delphi 2005
Aplikacje w Visual C++ 2005. Przykłady
Visual C# 2008. Projektowanie aplikacji. Pierwsze starcie
Visual Basic 2008. Warsztat programisty
Visual Basic 2005. Almanach
Visual Basic .NET. Bazy danych. Księga eksperta
Visual C# 2005. Zapiski programisty
Delphi 8 .NET. Kompendium programisty
Delphi dla .NET. Vademecum profesjonalisty
Visual C# 2005 Express Edition. Od podstaw
C#. Leksykon kieszonkowy
Delphi 7 dla każdego
Visual Basic 2005 Express i My Sql ebook
Wstęp do programowania w języku C#
Visual C++ 2005 Express Edition. Tworzenie aplikacji dla Windows

More from Wydawnictwo Helion (20)

PDF
Tworzenie filmów w Windows XP. Projekty
PDF
Blog, więcej niż internetowy pamiętnik
PDF
Access w biurze i nie tylko
PDF
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
PDF
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
PDF
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
PDF
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
PDF
Makrofotografia. Magia szczegółu
PDF
Windows PowerShell. Podstawy
PDF
Java. Efektywne programowanie. Wydanie II
PDF
JavaScript. Pierwsze starcie
PDF
Ajax, JavaScript i PHP. Intensywny trening
PDF
PowerPoint 2007 PL. Seria praktyk
PDF
Excel 2007 PL. Seria praktyk
PDF
Access 2007 PL. Seria praktyk
PDF
Word 2007 PL. Seria praktyk
PDF
Serwisy społecznościowe. Budowa, administracja i moderacja
PDF
AutoCAD 2008 i 2008 PL
PDF
Bazy danych. Pierwsze starcie
PDF
Inventor. Pierwsze kroki
Tworzenie filmów w Windows XP. Projekty
Blog, więcej niż internetowy pamiętnik
Access w biurze i nie tylko
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Makrofotografia. Magia szczegółu
Windows PowerShell. Podstawy
Java. Efektywne programowanie. Wydanie II
JavaScript. Pierwsze starcie
Ajax, JavaScript i PHP. Intensywny trening
PowerPoint 2007 PL. Seria praktyk
Excel 2007 PL. Seria praktyk
Access 2007 PL. Seria praktyk
Word 2007 PL. Seria praktyk
Serwisy społecznościowe. Budowa, administracja i moderacja
AutoCAD 2008 i 2008 PL
Bazy danych. Pierwsze starcie
Inventor. Pierwsze kroki

VB .NET. Almanach

  • 1. IDZ DO PRZYK£ADOWY ROZDZIA£ SPIS TRE CI VB .NET. Almanach Autorzy: Steve Roman, Ron Petrusha, Paul Lomax KATALOG KSI¥¯EK T³umaczenie: Dorota Bednarz, Krzysztof Jurczyk, Dariusz Ma³yszko KATALOG ONLINE ISBN: 83-7197-730-1 Tytu³ orygina³u: VB .NET Language In A Nutshell ZAMÓW DRUKOWANY KATALOG Format: B5, stron: 754 TWÓJ KOSZYK DODAJ DO KOSZYKA W dziesiêæ lat po powstaniu jêzyka Visual Basic firma Microsoft wprowadza na rynek platformê .NET z ca³kowicie poprawion¹ i przebudowan¹ wersj¹ tego jêzyka, opatrzon¹ nazw¹ Visual Basic .NET. Zdaniem niektórych jest to ca³kiem nowy jêzyk CENNIK I INFORMACJE programowania. Visual Basic jest teraz w pe³nym tego s³owa znaczeniu jêzykiem zorientowanym obiektowo — z d³ugo oczekiwanym dziedziczeniem klas i innymi ZAMÓW INFORMACJE elementami charakteryzuj¹cymi programowanie obiektowe. O NOWO CIACH W wiêkszo ci ksi¹¿ek po wiêconych Visual Basicowi zak³ada siê, ¿e czytelnik jest ca³kowitym nowicjuszem w dziedzinie programowania i dlatego s¹ one w du¿ej czê ci ZAMÓW CENNIK po wiêcone wprowadzeniu go w takie pojêcia, jak zmienne, ³añcuchy i instrukcje. Niniejszy almanach jest zupe³nie innym rodzajem ksi¹¿ki. Stanowi szczegó³owe, profesjonalne ród³o informacji o jêzyku VB .NET, do którego mo¿na siê odwo³aæ, CZYTELNIA by od wie¿yæ informacje na temat konkretnego elementu jêzyka czy parametru. Ksi¹¿ka bêdzie doskona³¹ pomoc¹ podczas programowania, kiedy zaistnieje potrzeba FRAGMENTY KSI¥¯EK ONLINE przejrzenia regu³ dotycz¹cych stosowania konkretnego elementu sk³adowego jêzyka lub wtedy, gdy nale¿y sprawdziæ czy nie przeoczono jakiego istotnego szczegó³u zwi¹zanego z konkretnym elementem jêzyka. W ksi¹¿ce VB .NET. Almanach omówiono m.in.: • Podstawowe typy danych jêzyka Visual Basic oraz sposób ich wykorzystania, a tak¿e typy danych .NET • Programowanie obiektowe w VB .NET • Nowe elementy sk³adowe .NET Framework, maj¹ce najwiêkszy wp³yw na sposób programowania w VB .NET, bibliotekê klas .NET Framework • Delegacje, zdarzenia i obs³ugê b³êdów w VB .NET • Wszystkie funkcje, instrukcje, dyrektywy, obiekty i elementy sk³adowe obiektów Wydawnictwo Helion tworz¹ce jêzyk VB .NET ul. Chopina 6 • Pu³apki czyhaj¹ce na programistê VB .NET i wiele przydatnych „tricków” 44-100 Gliwice programistycznych tel. (32)230-98-63 e-mail: helion@helion.pl
  • 2. 5RKU VTG EK Wstęp ....................................................................................................................... 5 Część I Podstawy...............................................................................13 Rozdział 1. Wprowadzenie................................................................................... 15 Dlaczego Visual Basic .NET? ................................................................................................... 16 Czym jest VB .NET? ................................................................................................................. 20 Co mogę zrobić w VB .NET? ................................................................................................... 26 Rozdział 2. Zmienne i typy danych ..................................................................... 27 Zmienne ................................................................................................................................... 27 Deklaracje zmiennych i stałych ................................................................................................ 32 Typy danych............................................................................................................................. 34 Tablice ...................................................................................................................................... 51 Zmienne obiektowe i ich wiązanie........................................................................................... 55 Obiekt Collection...................................................................................................................... 57 Parametry i argumenty ............................................................................................................ 58 Rozdział 3. Wprowadzenie do programowania obiektowego ............................ 65 Dlaczego programowanie obiektowe?..................................................................................... 65 Podstawy programowania obiektowego ................................................................................. 66 Klasy i obiekty.......................................................................................................................... 71 Dziedziczenie ........................................................................................................................... 78 Interfejsy, abstrakcyjne składowe i klasy ................................................................................. 84 Polimorfizm i przeciążanie....................................................................................................... 87 Zasięg i dostęp w module klasy .............................................................................................. 88
  • 3. 4 Spis treści Rozdział 4. .NET Framework — podstawowe pojęcia ....................................... 91 Przestrzenie nazw .................................................................................................................... 91 CLR (Common Language Runtime), kod zarządzany i dane zarządzane............................... 92 Nadzorowane wykonanie ........................................................................................................ 93 Pakiety...................................................................................................................................... 94 Pakiety a VB .NET.................................................................................................................... 95 Rozdział 5. Biblioteka klas .NET Framework..................................................... 99 Przestrzeń nazw System ........................................................................................................ 100 Pozostałe przestrzenie nazw .................................................................................................. 106 Rozdział 6. Delegacje i zdarzenia ...................................................................... 113 Delegacje ................................................................................................................................ 113 Zdarzenia i ich wiązanie ........................................................................................................ 117 Rozdział 7. Obsługa błędów w VB .NET........................................................... 121 Wykrywanie i obsługa błędów .............................................................................................. 121 Obsługa błędów czasu wykonania......................................................................................... 122 Obsługa błędów logicznych ................................................................................................... 131 Kody błędów.......................................................................................................................... 133 Część II Leksykon............................................................................. 135 Rozdział 8. Słownik języka VB .NET ................................................................ 137 Część III Dodatki ............................................................................. 661 Dodatek A Nowości i zmiany w VB .NET ........................................................ 663 Dodatek B Elementy języka — podział na kategorie ....................................... 681 Dodatek C Operatory ......................................................................................... 699 Dodatek D Stałe i wyliczenia ............................................................................ 709 Dodatek E Kompilator VB .NET uruchamiany z wiersza poleceń................... 719 Dodatek F Elementy języka VB 6 nieobsługiwane przez VB .NET .................. 727 Skorowidz ............................................................................................................ 731
  • 4. 3 Wprowadzenie do programowania obiektowego Ten rozdział jest krótkim i zwięzłym wprowadzeniem do programowania obiektowego. Ponieważ nie jest to książka o programowaniu obiektowym, skupimy się na tych zagad- nieniach, które są ważne podczas programowania w VB .NET. Dlaczego programowanie obiektowe? Począwszy od wersji 4 Visual Basic umożliwia stosowanie szeregu technik programowania obiektowego. Jednak niejednokrotnie prezentowano pogląd, że dotychczasowe wersje języka Visual Basic nie są „prawdziwym” obiektowym językiem. Dopiero w VB .NET zmiany wprowadzone w dziedzinie obiektowości są naprawdę zauważalne. Bez względu na prezentowane w tej kwestii stanowisko wydaje się oczywiste, że VB .NET jest obiek- towym językiem programowania w pełnym tego słowa znaczeniu. Można w tym miejscu powiedzieć: „Nie chcę używać technik programowania obiektowego w moich programach.”. W przypadku VB 6 było to jeszcze możliwe. Jednak w VB .NET struktura .NET Framework — szczególnie biblioteka klas Base Class Library — jak również dokumentacja jest całkowicie zorientowana obiektowo. Z tego powodu nie można dłużej unikać możliwości poznania podstaw programowania obiektowego nawet wtedy, gdy zdecydujemy się nie używać tych technik we własnych aplikacjach.
  • 5. 66 Rozdział 3. Wprowadzenie do programowania obiektowego Podstawy programowania obiektowego W literaturze często podaje się, że u podstaw programowania obiektowego leżą cztery główne pojęcia: • hermetyzacja; • abstrakcja; • dziedziczenie; • polimorfizm. Każde z powyższych pojęć w charakterystyczny dla siebie sposób odgrywa znaczącą rolę w programowaniu w VB .NET. Hermetyzacja oraz abstrakcja są „teoretycznymi” pojęciami stanowiącymi podstawę programowania obiektowego. Dziedziczenie i poli- morfizm stanowią pojęcia bezpośrednio stosowane podczas programowania w VB .NET. Abstrakcja Pojęcie abstrakcja oznacza po prostu przedstawienie danego elementu — encji — zawie- rające jedynie te jego aspekty, które są ważne w konkretnej sytuacji. Przypuśćmy, że chcemy utworzyć komponent odpowiedzialny za przechowywanie informacji o pracownikach przedsiębiorstwa. W tym celu rozpoczniemy od utworzenia listy pozycji istotnych dla naszej encji (pracownika przedsiębiorstwa). Niektóre z tych pozycji to: • imię i nazwisko; • adres; • numer identyfikacyjny pracownika; • pobory; • zwiększenie poborów; • zmniejszenie poborów. Ważne jest, że dołączyliśmy nie tylko właściwości encji (tj. pracowników), takie jak imię i nazwisko, ale również akcje, które możemy wykonać na tych encjach, jak na przykład zwiększenie lub zmniejszenie poborów. Wymienione czynności lub działania nazywane są również metodami, operacjami lub zachowaniami. W tej książce będziemy używać terminu metody, który jest powszechnie stosowany w VB .NET. Oczywiście nie będziemy tworzyć właściwości IQ określającej iloraz inteligencji pracow- nika, ponieważ nie jest to stosowne (by nie wspomnieć o dyskryminacyjnym charakterze takiego postępowania). Nie będziemy również dołączać właściwości „kolor włosów.” Wprawdzie ta cecha wchodzi w skład encji, jednak nie jest w tym przypadku istotna. Podsumowując — utworzyliśmy abstrakcyjne pojęcie pracownika, które zawiera jedynie te właściwości i metody, które nas interesują. Po utworzeniu takiego abstrakcyjnego modelu można przystąpić do hermetyzacji jego właściwości i metod w konkretnym komponencie.
  • 6. Podstawy programowania obiektowego 67 Hermetyzacja Hermetyzacja oznacza zawieranie właściwości i metod danego abstrakcyjnego modelu i udostępnianie na zewnątrz jedynie tych z nich, które są absolutnie konieczne. Każda właściwość i metoda modelu abstrakcyjnego nazywana jest jego elementem. Zbiór udo- stępnianych na zewnątrz elementów składowych modelu abstrakcyjnego (lub kompo- nentu zawierającego model abstrakcyjny) określa się zbiorowym terminem interfejsu publicznego (lub po prostu interfejsu). Hermetyzacja spełnia trzy główne zadania: • umożliwia ochronę właściwości i metod przed dostępem z zewnątrz; • umożliwia kontrolę poprawności wprowadzanych danych w interfejsie publicznym (na przykład sprawdzenie tego, czy nie zostaje przypisana zarobkom pracownika ujemna liczba); • zwalnia użytkownika od konieczności wnikania w szczegóły implementacyjne właściwości i metod. Weźmy jako przykład typ danych Integer, do którego dostęp jest starannie kontrolo- wany przez VB. Z pewnością wiadomo, że liczba całkowita jest przechowywana w pamięci komputera w postaci binarnego ciągu zer i jedynek. W VB liczby całkowite reprezentowane są w formie dopełnienia do dwu, dzięki czemu możliwe jest przechowywanie liczb ujem- nych i dodatnich. W celu uproszczenia rozważań weźmy pod uwagę 8-bitowe liczby całkowite. Liczba całkowita 8-bitowa ma postać a7a6a5a4a3a2a1a0, gdzie każdy element a o danym indeksie jest zerem lub jedynką. Można podać następującą reprezentację graficzną takiej liczby: Rysunek 3.1. 8-bitowa liczba binarna W liczbie, która zostanie zapisana w postaci dopełnienia do dwu, bit znajdujący się naj- bardziej na lewo — a7 (nazywany również najbardziej znaczącym bitem) — jest bitem znaku. Jeżeli bit znaku zawiera wartość 1, to liczba jest ujemna. W przeciwnym razie, gdy bit znaku zawiera 0, liczba jest dodatnia. Przy zamianie liczby a7a6a5a4a3a2a1a0 zapisanej w postaci dopełnienia do dwu na postać dziesiętną stosowany jest następujący wzór: postać dziesiętna = –128a7 + 64a6 + 32a5 + 16a4 + 8a3 + 4a2 + 2a1 + a0 Utworzenie liczby o przeciwnym znaku do danej liczby, która jest zapisana w postaci dopełnienia do dwu, polega na zmianie wartości każdego bitu na wartość przeciwną (tzn. każde 0 zamieniamy na 1, a każde 1 na 0), po czym do powstałej liczby dodaje się 1.
  • 7. 68 Rozdział 3. Wprowadzenie do programowania obiektowego W tym miejscu można powiedzieć: „Jako programista nie muszę zaprzątać sobie głowy takimi szczegółami. Wystarczy, że napiszę: x = -16 y = -x a kompilator niech wybierze odpowiednią reprezentację liczby i wykona wymagane operacje.”. Właśnie o to chodzi w hermetyzacji. Szczegóły interpretacji przez komputer (i kompilator) liczb całkowitych ze znakiem oraz implementacja ich właściwości i operacji na nich wy- konywanych są hermetyzowane, czyli zamknięte w samym typie całkowitoliczbowym. W ten sposób powyższe informacje są przed użytkownikami tego typu ukryte. Mamy dostęp jedynie do tych właściwości i operacji, które są potrzebne do posługiwania się liczbami całkowitymi. Udostępniane na zewnątrz właściwości i metody tworzą publiczny interfejs dla typu Integer. Ponadto hermetyzacja chroni przed popełnianiem błędów. Powróćmy jeszcze na mo- ment do przedstawionego powyżej przykładu — jeżeli musielibyśmy sami zmienić znak liczby tworząc dopełnienia do dwu i na końcu dodając 1, moglibyśmy zapomnieć wy- konać którąś z tych operacji. Hermetyzowany typ danych sam sprawuje automatycznie nad tym kontrolę. Hermetyzacja posiada jeszcze jedną ważną cechę. Kod napisany z wykorzystaniem udo- stępnianego na zewnątrz interfejsu pozostaje aktualny — nawet po zmianie wewnętrz- nych mechanizmów implementacji typu Integer — tak długo, jak długo ten interfejs nie ulega zmianie. Jeżeli przeniesiemy teraz nasz kod do komputera, który przechowuje liczby całkowite w postaci dopełnienia do jednego, wtedy wewnętrzna procedura, która imple- mentuje operację zmiany znaku liczby całkowitej, będzie musiała się zmienić. Z punktu widzenia programisty nic się jednak nie zmienia. Poniższy fragment kodu: x = -16 y = -x jest nadal poprawny. Interfejsy W VB hermetyzacja realizowana jest poprzez tworzenie komponentów. Można utworzyć komponent hermetyzujący omawiany wcześniej abstrakcyjny modelu pracownika. W VB .NET realizację metod interfejsu stanowią funkcje. Natomiast każda z właściwości, jak zobaczymy w dalszej części tego rozdziału, jest implementowana jako prywatna zmienna z dwiema towarzyszącymi publicznymi funkcjami. Zmienna prywatna prze- chowuje wartość właściwości. Pierwsza z funkcji publicznych służy do pobierania war- tości właściwości, podczas gdy druga wykorzystywana jest do jej ustawiania. Wymie- nione dwie funkcje określa się czasami mianem metod udostępniających właściwości. Zbiór udostępnianych na zewnątrz funkcji (zwykłych metod oraz metod udostępniających) tworzy interfejs modelu abstrakcyjnego.
  • 8. Podstawy programowania obiektowego 69 Komponent może hermetyzować i udostępniać na zewnątrz więcej niż jeden model abs- trakcyjny (a zatem więcej niż jeden interfejs). Bardziej realistycznym przykładem może być komponent wzorujący się na pracownikach przedsiębiorstwa, który posiada interfejs IIdentification (pierwsza litera „I” jest skrótem od słowa interfejs) służący do celów identyfikacji. Wspomniany interfejs mógłby mieć następujące właściwości: imię i nazwi- sko, numer ubezpieczenia, numer prawa jazdy, wiek, znaki szczególne itd. Poza tym interfejsem, komponent mógłby również zawierać interfejs zwany IEducation opisujący wykształcenie pracownika. Ten drugi interfejs implementowałby takie właściwości jak: poziom wykształcenia, tytuły, ukończone szkoły itp. Interfejs każdego modelu abstrakcyjnego udostępnianego przez komponent określa się również jako interfejs komponentu. W ten sposób komponent Employee (pracownik) im- plementuje przynajmniej dwa interfejsy: IIdentification i IEducation. Należy pamiętać, że termin interfejs jest również używany do określenia zbioru wszystkich udo- stępnianych na zewnątrz właściwości i metod komponentu (w tym przypadku kompo- nent ma tylko jeden interfejs). Wracając do naszego abstrakcyjnego modelu opisującego pracownika — jego interfejs mógłby składać się z funkcji zaprezentowanych w tabeli 3.1. Podany interfejs jest oczywi- ście bardzo uproszczony, ale w zupełności wystarcza do zilustrowania omawianych pojęć. Tabela 3.1. Elementy składowe interfejsu Employee (pracownik) Typ Nazwa Właściwość FullName: GetFullName(), SetFullName() Właściwość Address: GetAdress(), SetAddres() Właściwość EmployeeID: GetEmployeeID(), SetEmployeeID() Właściwość Salary: GetSalary(), SetSalary() Metoda IncSalary() Metoda DecSalary() Chociaż używanie terminu interfejs w znaczeniu zbioru funkcji jest powszechne, to wiąże się jednak z pewnym problemem. Mianowicie chodzi o to, że podanie nazw wszystkich funkcji interfejsu (w sposób przedstawiony w tabeli) nie dostarcza pełnej informacji po- trzebnej do wywołania tych funkcji. Bardziej użyteczną definicją interfejsu jest podanie zbioru sygnatur publicznych funkcji komponentu. W celu wyjaśnienia tego zagadnienia przeanalizujemy jedno z najważniejszych rozgraniczeń w programowaniu obiektowym — rozróżnienie między deklaracją, a implementacją funkcji. Utwórzmy następującą funkcję sortującą: Function Sort(a() As Integer, iSize As Integer) As Boolean For i = 1 to iSize For j = i + 1 to isize If a(j) < a(i) Then swap a (i), a(j)
  • 9. 70 Rozdział 3. Wprowadzenie do programowania obiektowego Next j Next i Sort = True End Function Pierwszy wiersz kodu: Function Sort(a() As Integer, iSize As Integer) As Boolean jest deklaracją funkcji. Deklaracja funkcji zawiera informację o liczbie i typie pobieranych parametrów oraz o typie zwracanej przez funkcję wartości. Treść funkcji: For i = 1 to iSize For j = i + 1 to iSize If a(j) < a(i) Then swap a (i), a(j) Next j Next i Sort = True jest implementacją funkcji. Opisuje, jak funkcja wykonuje postawione przed nią zadania. Należy zauważyć, że jest możliwa zmiana sposobu implementacji funkcji bez zmiany deklaracji funkcji. W rzeczywistości podana implementacja funkcji sortuje tablicę a za pomocą prostego algorytmu sortowania przez selekcję, ale możemy zmienić ten algo- rytm na dowolnie wybrany, inny algorytm sortowania (sortowanie bąbelkowe, sortowa- nie przez wstawianie, sortowanie szybkie — quick sort itp.) Teraz przyjrzyjmy się programowi (klientowi), który ma wykorzystać funkcję Sort. Program wywołujący musi znać jedynie deklarację funkcji Sort, by móc ją wywołać. Nie powinien (a prawdopodobnie nie chce) znać szczegółów implementacyjnych. W ten sposób to deklaracja funkcji — a nie jej implementacja — tworzy interfejs funkcji Sygnatura funkcji jest nazwą funkcji i typem zwracanej przez nią wartości wraz z na- zwami i typami jej parametrów podanymi w prawidłowej kolejności. Deklaracja funkcji jest po prostu przejrzystym sposobem opisania sygnatury funkcji. Zgodnie z konwencją przyjętą przez Microsoft wartość zwracana przez funkcję nie jest częścią sygnatury funkcji. Wedle tej konwencji sygnatura jest tzw. sygnaturą argumentów. Przyczyny przy- jęcia takiej konwencji staną się bardziej zrozumiałe w dalszej części rozdziału, kiedy przejdziemy do omawiania przeciążania nazw. Jednak byłoby lepiej (jak zwykle), gdyby terminologia stosowana przez Microsoft była bardziej starannie przemyślana. Według tej specyficznej definicji interfejsu interfejs naszego komponentu Employee (pracownik) mógłby wyglądać następująco (przedstawiono jedynie jego część): Function GetFullName(iEmpID As Long) As String Sub SetFullName(lEmpID As Long, sName As String) … Sub IncSalary(sngPercent As Single) Sub decSalary(sngPercent As Single)
  • 10. Klasy i obiekty 71 Klasy i obiekty Klasa jest komponentem definiującym i implementującym jeden lub więcej interfejsów. Ściśle mówiąc — klasa nie musi implementować wszystkich składowych interfejsu (omówione to zostanie podczas opisu składowych abstrakcyjnych klasy). Wyrażając to ina- czej można powiedzieć, że klasa łączy dane, funkcje i typy w jeden nowy typ. Microsoft używa pojęcia typ również w odniesieniu do klas. Moduły klas VB .NET W Visual Studio.NET moduł klas VB wstawiany jest do projektu po wybraniu pozycji Add Class z menu Projekt. W ten sposób dołączony zostaje nowy moduł zawierający kod: Public Class ClassName End Class Visual Studio przechowuje każdą klasę w oddzielnym pliku, jednak nie jest to konieczne. Konstrukcja Class ...End Class zaznacza początek i koniec definicji klasy. W ten sposób w pojedynczym pliku źródłowym może znajdować się więcej niż jedna klasa, jak też może zostać umieszczonych jeden lub więcej modułów (ograniczonych konstrukcją Module...End Module). Klasa CPerson zdefiniowana w następnym podrozdziale jest przykładem wykorzystania modułu klas. Składowe klasy W VB .NET moduły klas mogą zawierać przedstawione niżej rodzaje elementów. Dane Znajdują się tutaj zmienne (również określane jako pola) oraz stałe. Zdarzenia Zdarzenia są procedurami wywoływanymi automatycznie przez Common Language Runtime w odpowiedzi na niektóre zachodzące akcje (takie jak na przykład tworzenie obiektu, naciśnięcie przycisku, zmiana danych lub wyjście obiektu z zasięgu). Funkcje i procedury Funkcje i procedury nazywane są również metodami. Konstruktor klasy jest specjalnym rodzajem metody. Konstruktory są szczegółowo omawiane w dalszej części tego rozdziału. Właściwości Właściwość jest implementowana jako prywatna zmienna wraz z dwiema specjalnymi funkcjami udostępniającymi. Składnia tych specjalnych funkcji jest omówiona w dalszej części tego rozdziału w podrozdziale „Właściwości.”
  • 11. 72 Rozdział 3. Wprowadzenie do programowania obiektowego Typy Składowa klasy może być również inną klasą (w tym przypadku określana jest jako klasa zagnieżdżona). Poniższa klasa CPerson zawiera kilka rodzajów składowych: Public Class CPerson ' ----------------- ' Dane ' ----------------- ' Zmienne składowe Private msName As String Private miAge As Integer ' Stała Public Const MAXAGE As Short = 120 ' Zdarzenie Public Event Testing() ' ----------------- ' Funkcje ' ----------------- ' Metody Public Sub Test() RaiseEvent Testing () End Sub Property Age() As Integer Get Age = miAge End Get Set(ByVal Value As Integer) ' Kontrola poprawności If Value < 0 Then MsgBox("Wiek nie mo e być liczbą ujemną") Else miAge = Value End If End Set End Property ' Właściwość Property Name() As String ' Metody udostępniające właściwości Get Name = msName End Get Set(ByVal Value As String) msName = Value End Set End Property Sub Dispose() ' Zwolnienie zasobów End Sub End Class
  • 12. Klasy i obiekty 73 Interfejs publiczny klasy VB .NET Podczas omawiania pojęć związanych z programowaniem obiektowym stwierdziliśmy, że udostępniane na zewnątrz składowe komponentu tworzą jego publiczny interfejs (lub po prostu interfejs). Ponadto w VB .NET każda składowa modułu klasy ma określony typ dostępu, którym może być Public, Private, Friend, Protected lub Protected Friend. Typy dostępu zostały szczegółowo omówione w dalszej części tego rozdziału. W tym miejscu wystarczy powiedzieć, że moduł klasy w VB .NET może mieć składowe typu Public, Private, Friend, Protected oraz Protected Friend. W ten sposób powstaje pewna dwuznaczność przy definiowaniu pojęcia interfejsu pu- blicznego klasy w VB .NET. Samo pojęcie mogłoby wskazywać, że należy uważać każdą składową udostępnianą na zewnątrz jako część publicznego interfejsu klasy. W tym przypadku pod pojęciem interfejsu publicznego klasy rozumielibyśmy oprócz składowych Public również składowe Protected, Friend oraz Protected Friend. Z drugiej strony — można by argumentować, że składowe interfejsu publicznego muszą być udo- stępniane na zewnątrz projektu zawierającego klasę, do którego omawiany interfejs na- leży. Przy takim zdefiniowaniu pojęcia interfejsem publicznym są jedynie jego składowe Public. Na szczęście nie musimy podawać dokładnej definicji interfejsu publicznego klasy w VB .NET, jeśli pamiętamy, że ten termin może być różnie definiowany. Obiekty Klasa przedstawia jedynie opis właściwości oraz metod, a tym samym nie jest jednostką posiadającą samodzielny byt (z wyjątkiem składowych współużytkowanych, które zostaną omówione w dalszej części książki.) W celu wywołania metod i wykorzystania właści- wości należy utworzyć instancję klasy, oficjalnie nazywaną obiektem. Tworzenie instancji klasy określane jest jako konkretyzacja lub ukonkretnienie klasy. Istnieją trzy sposoby utworzenia obiektu danej klasy w VB .NET. Jeden z nich polega na zadeklarowaniu zmiennej obiektowej reprezentującej daną klasę: Dim APerson As CPerson a następnie utworzeniu obiektu za pomocą słowa kluczowego New: APerson = New CPerson() Możemy obie czynności wykonać w jednym wierszu kodu: Dim APerson As New CPerson() lub: Dim APerson As CPerson = New CPerson() Pierwszy sposób zapisu uważany jest za skróconą formę drugiego zapisu.
  • 13. 74 Rozdział 3. Wprowadzenie do programowania obiektowego Właściwości Właściwości są składowymi klasy, które mogą być zaimplementowane na dwa różne sposoby. W najprostszej postaci właściwość jest jedynie publiczną zmienną: Public Class CPerson Public Age As Integer End Class W przypadku takiej implementacji właściwości Age problemem jest brak hermetyzacji. Dostęp do obiektu klasy CPerson daje możliwość ustawienia właściwości Age na do- wolną wartość typu Integer (w tym również na wartość ujemną, która jest nieprawi- dłową reprezentacją wieku). Nie ma tutaj możliwości kontrolowania poprawności wpro- wadzanych danych (a ponadto powyższa implementacja właściwości nie umożliwia jej włączenia do interfejsu publicznego klasy w myśl podanej przez nas definicji tego terminu). „Poprawnym” obiektowym sposobem implementacji właściwości jest utworzenie zmiennej typu Private wraz z dwiema funkcjami. Zmienna typu Private przecho- wuje wartość właściwości. Natomiast dwie funkcje składowe, które są zwane metodami udostępniającymi, służą do pobierania i ustawiania wartości właściwości. W ten sposób dane są hermetyzowane, a dostęp do właściwości może zostać ograniczony za pomocą funkcji udostępniających, które mogą kontrolować poprawność wprowadzanych danych. Poniższy fragment kodu implementuje właściwość Age: Private miAge As Integer Property Age() As Integer Get Age = miAge End Get Set(ByVal Value As Integer) ' Kontrola poprawności wprowadzanych danych If Value < 0 Then MsgBox("Wiek musi być liczbą dodatnią") Else miAge = Value End If End Set End Property Jak wynika z powyższego przykładu, Visual Basic ma specjalną składnię służącą do defi- niowania metod udostępniających właściwości. Natychmiast po wprowadzeniu w edytorze Visual Studio .NET poniższego wiersza kodu: Property Age() As Integer tworzony jest przez IDE następujący wzorzec: Property Age() As Integer Get End Get Set(ByVal Value As Integer) End Set End Property
  • 14. Klasy i obiekty 75 Należy zauważyć, że parametr Value umożliwia dostęp do wartości, która ma zostać wprowadzona. W poniższym fragmencie kodu: Dim cp As New CPerson() cp.Age = 20 wartość 20 zostaje przekazana do metody Set właściwości Age jako argument Value. Składowe obiektowe i współużytkowane Elementy składowe klasy można podzielić na dwie grupy. Składowe obiektowe Dostępne jedynie poprzez instancję, czyli obiekt danej klasy. Innymi słowy — składowe obiektowe „należą” do konkretnego obiektu, a nie do klasy jako całości. Składowe (statyczne) współużytkowane Dostęp do składowych współużytkowanych jest możliwy bez tworzenia obiektu danej klasy. Te składowe są współużytkowane między wszystkimi obiektami danej klasy. Mówiąc ściślej — są niezależne od jakiegokolwiek obiektu klasy. Składowe współużytkowane „należą” do klasy jako całości, a nie do jej poszczególnych obiektów (czyli instancji). Dostęp do składowych obiektowych wymaga podania przed nazwą składowej nazwy obiektu. Tak jak w poniższym fragmencie kodu: Dim APerson As New CPerson() APerson.Age = 50 Dostęp do składowych współużytkowanych wymaga podania nazwy klasy. Jako przykład omówimy klasę String w przestrzeni nazw System biblioteki klas .NET Base posia- dającą metodę współużytkowaną Compare, która porównuje dwa łańcuchy. Składnia metody Compare: Public Shared Function Compare(String, String) As Integer Metoda Compare w przypadku równych łańcuchów zwraca 0. W przeciwnym razie zwraca wartość –1, jeżeli pierwszy łańcuch jest mniejszy od drugiego (jest wcześniejszy w porządku alfabetycznym) i 1, gdy pierwszy łańcuch jest większy od drugiego. Ponieważ Compare jest metodą współużytkowaną możliwy jest zapis: Dim s As String = "steve" Dim t As String = "donna" MsgBox(String.Compare(s, t)) ' Wyświetla 1 Należy zauważyć, że nazwa metody Compare poprzedzona jest nazwą klasy String. Składowe współużytkowane są pomocne podczas kontroli danych niezależnych od wszystkich obiektów danej klasy. Załóżmy, że chcemy kontrolować liczbę istniejących obiektów klasy CPerson. Napiszmy następujący podprogram:
  • 15. 76 Rozdział 3. Wprowadzenie do programowania obiektowego ' Deklaracja zmiennej współu ytkowanej przechowującej liczbę obiektów klasy Private Shared miInstanceCount As Integer ' Zwiększenie wartości miInstanceCount w konstruktorze ' (Poni szy kod nale y dodać równie do pozostałych ' konstruktorów, o ile takowe zostały zadeklarowane) Sub New() miInstanceCount += 1 End Sub ' Utworzenie funkcji zwracającej liczbę obiektów Shared Function GetInstanceCount() As Integer Return miInstanceCount End Function ' Zmniejszenie wartości miInstanceCount w destruktorze Overrides Protected Sub Finalize() miInstanceCount -= 1 MyBase.Finalize End Sub Teraz za pomocą poniższego kodu mamy dostęp do zmiennej współużytkowanej: Dim steve As New CPerson() MsgBox(CPerson.GetInstanceCount) ' Wyświetla 1 Dim donna As New CPerson() MsgBox(CPerson.GetInstanceCount) ' Wyświetla 2 Konstruktory klas Podczas tworzenia obiektu danej klasy kompilator wywołuje specjalną funkcję zwaną konstruktorem klasy lub konstruktorem obiektu. Konstruktory mogą służyć do inicjowania obiektu, gdy zachodzi taka potrzeba (zastępują one zdarzenie Class_Initialize sto- sowane we wcześniejszych wersjach VB). Konstruktory można definiować w module klasy. Jeżeli nie zdefiniujemy żadnego konstruktora, to VB użyje konstruktora domyślnego. W poniższym fragmencie kodu: Dim APerson As CPerson = New CPerson() wywoływany jest domyślny konstruktor klasy CPerson, ponieważ nie zadeklarowaliśmy własnego konstruktora. Utworzenie własnego konstruktora polega na zdefiniowaniu procedury o nazwie New wewnątrz modułu klasy. Przypuśćmy, że chcemy podczas tworzenia obiektu klasy CPerson nadać jego właściwości Name konkretną wartość. Wtedy powinniśmy umieścić następujący kod w klasie CPerson: ' Konstruktor u ytkownika Sub New(ByVal sName As String) Me.Name = sName End Sub Teraz można utworzyć obiekt klasy CPerson i ustawić jego nazwę:
  • 16. Klasy i obiekty 77 Dim APerson As CPerson = New CPerson("fred") lub: Dim APerson As New CPerson("fred") Należy zauważyć, że VB .NET umożliwia przeciążanie (to zagadnienie omówiono w dalszej części tego rozdziału), więc możliwe jest zdefiniowanie wielu konstruktorów w tej samej klasie pod warunkiem, że każdy z nich ma unikatową sygnaturę argumentów. W takim przypadku można wywołać dowolny z nich, podając odpowiednią liczbę oraz odpowiedni dla tego konstruktora typ argumentów. Po zadeklarowaniu jednego lub większej ilości konstruktorów użytkownika nie jest możliwe wywołanie konstruktora domyślnego (tzn. bez parametrów) za pomocą nastę- pującej instrukcji: Dim APerson As New CPerson() W tym przypadku należy jawnie zadeklarować bezparametrowy konstruktor w module klasy: ' Konstruktor domyślny Sub New() … End Sub Finalize, Dispose i oczyszczanie pamięci W VB 6 programista ma możliwość zaimplementowania zdarzenia Class_Terminate w celu wykonania określonych czynności przed zniszczeniem obiektu. Jeżeli na przykład obiekt otworzył plik i przechowuje jego uchwyt, ważne jest, by zamknąć ten plik przed jego zniszczeniem. W VB .NET nie istnieje zdarzenie Class_Terminate przez co inny jest mechanizm obsługi tego typu sytuacji. W celu zrozumienia omawianych zagadnień należy najpierw omówić proces oczyszczania pamięci, zwany inaczej „odśmiecaniem”. Kiedy program odpowiedzialny za oczyszczanie pamięci stwierdzi, że obiekt nie jest już potrzebny (ma to na przykład miejsce, gdy wykonywany program nie zawiera już refe- rencji do tego obiektu), wtedy automatycznie wywoływana jest specjalna metoda destruktora pod nazwą Finalize. Ważne jest jednak, by zrozumieć, że w przeciwieństwie do zda- rzenia Class_Terminate nie ma teraz możliwości określenia dokładnego czasu wy- wołania przez program oczyszczający pamięć metody Finalize. Możemy być jedynie pewni, że zostanie ona wywołana jakiś czas po zwolnieniu ostatniego odwołania do obiektu. Opóźnienie wynika z faktu, że .NET Framework używa systemu pod nazwą odśmiecanie ze śledzeniem odwołań, który zwalnia okresowo nieużywane zasoby. Metoda Finalize jest metodą Protected. Oznacza to, że może być wywoływana je- dynie z klasy bazowej oraz jej klas pochodnych. Nie ma możliwości wywołania metody Finalize spoza klasy (klasa nie powinna praktycznie nigdy wywoływać swojej metody
  • 17. 78 Rozdział 3. Wprowadzenie do programowania obiektowego Finalize bezpośrednio, ponieważ destruktor Finalize wywoływany jest automa- tycznie przez program oczyszczający pamięć). Jeżeli utworzymy metodę Finalize danej klasy, to powinniśmy również jawnie w jej treści wywołać metodę Finalize jej klasy bazowej. W ten sposób składnia i format metody Finalize wygląda następująco: Overrides Protected Sub Finalize() ' Zaplanowane czynności MyBase.Finalize End Sub Korzyści wynikające z tego typu oczyszczania pamięci polegają na automatyzacji tego procesu oraz zapewnieniu tego, że niewykorzystane zasoby zostaną na pewno uwolnio- ne bez jakiejkolwiek ingerencji ze strony programisty. Natomiast wadą jest brak możli- wości bezpośredniego zainicjowania oczyszczania pamięci przez aplikację, przez co nie- które zasoby mogą pozostać w użyciu dłużej, niż to jest konieczne. Mówiąc prosto z mostu: nie możemy zniszczyć obiektu na żądanie. Należy wziąć pod uwagę, że nie wszystkie zasoby są zarządzane przez Common Lan- guage Runtime. Niektóre zasoby, takie jak na przykład uchwyty okien i połączenia z bazami danych, nie podlegają automatycznemu oczyszczaniu. Dlatego w celu ich zwolnienia należy dołączyć odpowiedni kod w metodzie Finalize. Ten sposób nie umożliwia jednak zwalniania zasobów na żądanie. Do tego celu służy drugi destruktor o nazwie Dispose, który jest zdefiniowany w bibliotece klas Base. Destruktor Dispose ma nastę- pującą składnię: Class classname Implements IDisposable Public Sub Dispose() Implements IDisposable.Dispose ' Zaplanowane czynności (np. zwalnianie zasobów) ' Wywołanie metod Dispose obiektów potomnych, je eli to jest konieczne End Sub ' Pozostałe składowe klasy End Class Należy zauważyć, że klasy wykorzystujące ten rodzaj destruktora muszą implementować interfejs IDisposable — z tego powodu konieczna jest instrukcja Implements pokazana powyżej. Interfejs IDisposable ma tylko jedną składową, a jest nią metoda Dispose. Konieczne jest poinformowanie użytkowników klasy, że muszą oni wywoływać tę metodę jawnie w celu zwolnienia zasobów (technicznym terminem jest tutaj określenie ręczne podejście!). Dziedziczenie Najlepszym sposobem opisania mechanizmu dziedziczenia zastosowanego w VB .NET będzie podanie następującego przykładu. Klasy wykorzystywane przez aplikację często są powiązane między sobą. Weźmy jako przykład dane odnoszące się do pracowników. Wspólne dane wszystkich pracowników
  • 18. Dziedziczenie 79 reprezentowane są przez obiekty Employee (pracownik) klasy CEmployee — można tutaj wymienić imię, nazwisko, nazwisko, adres, pobory itd. Dodatkowe zarobki kierownictwa przedsiębiorstwa będą oczywiście inne niż analogiczne zarobki, powiedzmy, osoby pracującej na stanowisku sekretarki. Z tego względu rozsąd- nie byłoby zdefiniować dodatkowe klasy CExecutive i CSecretary, każdą ze swoimi własnymi właściwościami i metodami. Z drugiej strony — kierownik, to także pracow- nik i nie ma powodu definiowania dwu różnych właściwości Name w tym przypadku. Takie postępowanie jest nieefektywne i prowadzi do marnowania zasobów. W tym właśnie celu stosuje się dziedziczenie. Najpierw zdefiniujemy klasę CEmployee, która implementuje właściwość Salary i metodę IncSalary: ' Klasa Employee Public Class CEmployee ' Właściwość Salary umo liwia zapis / odczyt Private mdecSalary() As Decimal Property Salary() As Decimal Get Salary = mdecSalary End Get Set(ByVal Value as Decimal) mdecSalary = Value End Set End Property Public Overridable Sub IncSalary(ByVal sngPercent As Single) mdecSalary = mdecSalary * (1 + CDec(sngPercent)) End Sub End Class Następnie definiujemy klasę CExecutive: ' Klasa CExecutive Public Class CExecutive Inherits CEmployee ' Oblicz wzrost zarobków uwzgledniąjacy 5% dodatku na samochód Overrides Sub IncSalary(ByVal sngPercent As Single) Me.Salary = Me.Salary * CDec(1.05 + sngPercent) End Sub End Class Należy zwrócić uwagę na dwie kwestie. Po pierwsze instrukcja: Inherits CEmployee określa, że klasa CExecutive dziedziczy składowe klasy CEmployee. Mówiąc inaczej — obiekt klasy CExecutive jest również obiektem klasy CEmployee. Zatem jeżeli utworzymy obiekt klasy CExecutive: Dim ceo As New CExecutive to mamy dostęp do właściwości Salary: ceo.Salary = 1000000
  • 19. 80 Rozdział 3. Wprowadzenie do programowania obiektowego Po drugie — słowo kluczowe Overrides w metodzie IncSalary oznacza, że metoda IncSalary klasy CExecutive jest wywoływana zamiast metody zaimplementowanej w CEmployee. Zatem kod: ceo.IncSalary zwiększa pobory obiektu ceo klasy CExecutive uzględniając dodatek na samochód. W deklaracji metody IncSalary klasy CEmployee znajduje się słowo kluczowe Overridable oznaczające, że klasy dziedziczące od klasy podstawowej mogą prze- słaniać odnośną metodę klasy bazowej. Następnie definiujemy nową klasę CSecretary, która też dziedziczy z klasy CEmployee, ale implementuje inny rodzaj dodatku do pensji: ' Klasa CSecretary Public Class CSecretary Inherits CEmployee ' Sekretarka otrzymuje 2% dodatek za nadgodziny Overrides Sub IncSalary(ByVal sngPercent As Single) Me.Salary = Me.Salary * CDec(1.02 + sngPercent) End Sub End Class Napiszemy teraz kod wykorzystujący powyższe klasy: ' Utworzenie nowych obiektów Dim ThePresident As New CExecutive() Dim MySecretary As New CSecretary() ' Ustalenie zarobków ThePresident.Salary = 1000000 MySecretary.Salary = 30000 ' Wyświetl zarobki przewodniczącego i sekretarki bez oraz z dodatkiem Debug.WriteLine("Prezes przed: " & CStr(ThePresident.Salary)) ThePresident.IncSalary(0.4) Debug.WriteLine("Prezes po: " & CStr(ThePresident.Salary)) Debug.WriteLine("Sekretarka przed: " & CStr(MySecretary.Salary)) MySecretary.IncSalary(0.4) Debug.WriteLine("Sekretarka po: " & CStr(MySecretary.Salary)) Wynik wykonania programu: Prezes przed: 1000000 Prezes po: 1450000 Sekretarka przed: 30000 Sekretarka po: 42600 Dziedziczenie jest stosunkowo prostym pojęciem. W dokumentacji Microsoftu znajduje się taki jego opis: Jeżeli Klasa B dziedziczy od Klasy A, to każdy obiekt Klasy B jest również obiektem Klasy A i zawiera publiczne metody i właściwości (tzn. interfejs publiczny) Klasy A. W tym przypadku Klasa A nazywana jest klasą bazową, a Klasa B klasą potomną. Klasa potomna może przesłaniać składowe klasy podstawowej zgodnie ze swoimi potrzebami. W poprzednim przykładzie zauważyliśmy, że słowem kluczowym definiującym dziedzi- czenie jest Inherits.
  • 20. Dziedziczenie 81 Pozwolenie na dziedziczenie Istnieją dwa słowa kluczowe, używane w deklaracji klasy bazowej, które określają moż- liwość dziedziczenia z tej klasy. NotInheritable Użycie tego słowa kluczowego w deklaracji klasy: Public NotInheritable Class InterfaceExample powoduje, że klasa nie może być klasą bazową. MustInherit Użycie tego słowa kluczowego w deklaracji klasy: Public MustInherit Class InterfaceExample sprawia, że obiekty tej klasy nie mogą być tworzone bezpośrednio. Mogą być natomiast tworzone obiekty klas potomnych. Innymi słowy — klasy MustInherit mogą być klasami bazowymi i tylko klasami bazowymi. Przesłanianie Istnieje kilka słów kluczowych określających to, czy (i w jaki sposób) klasa potomna może przesłaniać implementację klasy bazowej. Omawiane słowa kluczowe używane są w de- klaracji odnośnej składowej, a nie w deklaracji klasy. Overridable Zezwala na przesłanianie, jednak nie wymaga, by składowa była przesłaniana. Domyślnym ustawieniem dla składowej Public jest NotOverridable: Public Overridable Sub IncSalary() NotOverridable Zabrania przesłaniania danej składowej. Domyślne ustawienie dla składowych Public klasy. MustOverride Składowa musi zostać przesłonięta. W przypadku użycia tego słowa kluczowego nie podajemy w deklaracji składowej implementacji i nie używamy słów kluczowych End Sub i End Function: Public MustOverride Sub IncSalary() Jeżeli klasa zawiera składową ze słowem kluczowym MustOverride, to musi zostać zadeklarowana jako klasa MustInherit. Overrides W przeciwieństwie do poprzednich modyfikatorów ten modyfikator stosowany jest w przypadku składowych klas potomnych i wskazuje, że modyfikowana składowa przesłania składową klasy bazowej: Overrides Sub IncSalary()
  • 21. 82 Rozdział 3. Wprowadzenie do programowania obiektowego Reguły dziedziczenia W wielu językach obiektowych, takich jak na przykład w C++, klasa może dziedziczyć bezpośrednio z więcej niż jednej klasy bazowej. Określane jest to dziedziczeniem wielo- krotnym. W VB .NET nie jest możliwe dziedziczenie wielokrotne. W ten sposób klasa może bezpośrednio dziedziczyć najwyżej z jednej klasy. Poniższy fragment kodu nie jest więc poprawny: ' Klasa Executive Public Class CExecutive ' NIEPOPRAWNE Inherits CEmployee Inherits CWorker … End Class Natomiast Klasa C może dziedziczyć z Klasy B, która z kolei może dziedziczyć z Klasy A. W ten sposób możliwe jest utworzenie hierarchii dziedziczenia. Klasa może również implementować wiele interfejsów za pomocą słowa kluczowego Interface. Powyższe zagadnienie jest omawiane w dalszej części tego rozdziału. MyBase, MyClass i Me Słowo kluczowe MyBase umożliwia dostęp do klasy bazowej z klasy potomnej. W celu wywołania składowej klasy bazowej z klasy potomnej należy użyć następującej składni: MyBase.MemberName gdzie w miejscu MemberName podaje się nazwę składowej klasy, do której mamy się odwołać. W ten sposób nie powstaje żadna dwuznaczność w przypadku, gdy klasa po- tomna ma również składową o tej samej nazwie. Słowo kluczowe MyBase może zostać użyte do wywołania konstruktora klasy bazowej przy tworzeniu obiektu klasy potomnej: MyBase.New(…) Nie można za pomocą MyBase wywoływać składowych klasy o dostępie Private. W przypadku użycia słowa kluczowego MyBase wyszukiwana jest składowa znajdująca się najbliżej w drzewie dziedziczenia. Zatem jeżeli klasa C dziedziczy od klasy B, która dziedziczy od klasy A, to wywołanie procedury AProc w klasie C: MyBase.AProc powoduje, że najpierw przeszukiwana jest klasa B w celu znalezienia procedury o nazwie AProc. Jeżeli w tej klasie nie zostanie znaleziona poszukiwana procedura, to VB .NET przeszuka klasę A, szukając odpowiedniej procedury (pod pojęciem odpowiedniej proce- dury rozumiemy procedurę o takiej samej nazwie i sygnaturze argumentów).
  • 22. Dziedziczenie 83 Słowo kluczowe MyClass umożliwia dostęp do klasy, w której zostało użyte. MyClass jest podobne do słowa kluczowego Me. Jedyna różnica występuje przy wywoływaniu metod. W celu zilustrowania tego zagadnienia utworzymy klasę Class1 i klasę pochodną o nazwie Class1Derived. Obie klasy posiadają metodę IncSalary: Public Class Class1 Public Overridable Function IncSalary(ByVal sSalary As Single) _ As Single IncSalary = sSalary * CSng(1.1) End Function Public Sub ShowSalary(ByVal sSalary As Single) MsgBox(Me.IncSalary(sSalary)) MsgBox(MyClass.IncSalary(sSalary)) End Sub End Class Public Class Class1Derived Inherits Class1 Public Overrides Function IncSalary(ByVal sSalary As Single) _ As Single IncSalary = sSalary * CSng(1.2) End Function End Class Przeanalizujmy teraz poniższy fragment kodu: Dim c1 As New Class1() Dim c2 As New Class1Derived() Dim clvar As Class1 clvar = c1 clvar.ShowSalary(10000) ' Wyświetla 11000, 11000 clvar = c2 clvar.ShowSalary(10000) ' Wyświetla 12000, 11000 Pierwsze wywołanie metody IncSalary wykorzystuje zmienną typu Class1, która jest referencją do obiektu typu Class1. W tym przypadku oba poniższe wywołania: Me.IncSalary MyClass.IncSalary zwracają taką samą wartość, ponieważ wywołują metodę IncSalary w klasie bazowej Class1. Jednak za drugim razem zmienna typu Class1 zawiera referencję do obiektu klasy potomnej — Class1Derived. Teraz Me odnosi się do obiektu typu Class1Derived, podczas gdy MyClass nadal wskazuje klasę bazową Class1, w której słowo kluczowe MyClass występuje. W ten sposób: Me.IncSalary zwraca 12000, natomiast: MyClass.IncSalary zwraca 11000.
  • 23. 84 Rozdział 3. Wprowadzenie do programowania obiektowego Interfejsy, abstrakcyjne składowe i klasy W trakcie wcześniejszych rozważań wspomnieliśmy już, że klasa może implementować wszystkie składowe interfejsu, część lub może nie implementować żadnej składowej inter- fejsów, które deklaruje. Składowa interfejsu, która nie zawiera swojej implementacji określana jest jako składowa abstrakcyjna. Zadaniem składowej abstrakcyjnej jest podanie sygnatury składowej (wzorca), która może zostać zaimplementowana przez jedną lub więcej klas potomnych w różny sposób w każdej z nich. Wyjaśnimy powyższe zagadnienie na przykładzie. Przypomnijmy, że klasa CEmployee deklaruje i implementuje metodę IncSalary zwiększającą pobory pracownika. Ponadto dodajmy, że klasy potomne CExecutive i CSecretary przesłaniają implementację metody IncSalary klasy bazowej CEmployee. Przypuśćmy, że utworzyliśmy bardziej rozbudowany model pracowników, w którym każdemu stanowisku przyporządkowano oddzielną klasę pochodną. Każda z tych klas przesłania implementację metody IncSalary klasy bazowej CEmployee. W tym przy- padku implementacja metody IncSalary klasy bazowej nie musi być nigdy wywoły- wana. Po co więc podawać implementację metody, która nigdy nie będzie wykorzystana? Zamiast tego, możemy po prostu utworzyć metodę IncSalary z pustym „ciałem”: ' Klasa Pracownik Public Class CEmployee … Public Overridable Sub IncSalary(ByVal sngPercent As Single) End Sub End Sub Jeżeli wszystkie klasy pochodne muszą implementować metodę IncSalary, wtedy należy użyć słowa kluczowego MustOverride: ' Klasa pracownik Public MustInherit Class CEmployee … Public MustOverride Sub IncSalary(ByVal sngPercent As Single) End Class Jak już stwierdziliśmy, nie ma skojarzonej instrukcji End Sub ze słowem kluczo- wym MustOverride. Należy również pamiętać, że słowo kluczowe MustOverride wymaga, by klasa, w której zostało użyte, zadeklarowana została ze słowem klu- czowego MustInherit. W ten sposób stwierdzamy, że nie wolno bezpośrednio tworzyć obiektów klasy CEmployee. W powyższych przykładach składowa IncSalary klasy bazowej CEmployee jest skła- dową abstrakcyjną. Klasa z przynajmniej jedną składową abstrakcyjną nazywana jest klasą abstrakcyjną. Zatem zdefiniowana powyżej klasa CEmployee jest klasą abstrakcyjną. Zastosowana termino-
  • 24. Interfejsy, abstrakcyjne składowe i klasy 85 logia wynika z faktu, że nie jest możliwe utworzenie obiektu klasy abstrakcyjnej, ponieważ przynajmniej jedna z metod obiektu nie posiadałaby implementacji. W pewnych sytuacjach może nam być potrzebna klasa, której wszystkie składowe są abstrakcyjne. Innymi słowy — jest to klasa, która jedynie definiuje interfejs. Taką klasę można określić mianem klasy czysto abstrakcyjnej, chociaż nie jest to ogólnie przyjęte sformułowanie. Jako przykład weźmy klasę określającą kształt zwaną CShape, której celem jest przed- stawienie ogólnych właściwości i operacji, które mogą być wykonywane na figurach geometrycznych (elipsach, prostokątach, trapezoidach itp.). Każdy kształt czy figura wymaga metody Draw do narysowania samej siebie. Implementacja takiej metody bę- dzie różna dla różnych rodzajów figur — na przykład inaczej rysujemy okręgi, a inaczej prostokąty. W identyczny sposób będziemy musieli jednak dołączyć metody, takie jak Rotate (obrót), Translate (przesunięcie) i Reflect (odbicie). Każda z tych metod będzie posiadała inną implementację (w zależności od reprezentowanej figury czy kształtu). Zatem możemy zdefiniować klasę CShape na dwa sposoby: Public Class Class2 Public Overridable Sub Draw() End Sub Public Overridabe Sub Rotate(ByVal sngDegrees As Single) End Sub Public Overridabe Sub Translate(ByVal x As Integer, _ ByVal y As Integer) End Sub Public Overridable Sub Reflect(ByVal iSlope As Integer, _ ByVal iIntercept As Integer) End Sub End Class lub: Public MustInherit Class CShape Public MustOverride Sub Draw() Public MustOverride Sub Rotate(ByVal sngDegresss As Single) Public MustOverride Sub Rotate(ByVal x As Integer, _ ByVal y As Integer) Public MustOverride Sub Reflect(ByVal iSlope As Integer, _ ByVal iIntercept As Integer) End Class Teraz możemy zdefiniować klasy potomne, takie jak CRectangle, CEllipse, CPoly- gon itp. Każda z tych klas będzie implementować (lub musi implementować w drugim przypadku) składowe klasy bazowej CShape. Nie omawiamy tutaj jednak szczegółów takiej implementacji, gdyż nie stanowi to głównego tematu naszych rozważań.
  • 25. 86 Rozdział 3. Wprowadzenie do programowania obiektowego Interfejsy raz jeszcze Powiedzieliśmy, że interfejsy mogą być zdefiniowane w modułach klas. W VB .NET moż- liwy jest również inny sposób definiowania interfejsu za pomocą słowa kluczowego Interface. Poniżej zdefiniowano interfejs IShape: Public Interface IShape Sub Draw() Sub Rotate(ByVal sngDegrees As Single) Sub Translate(ByVal x As Integer, ByVal y As Integer) Sub Reflect(ByVal iSlope As Integer, _ ByVal iIntercept As Integer) End Interface W przypadku zastosowania słowa kluczowego Interface nie można implementować składowych wewnątrz modułu, w którym zdefiniowano interfejs. Można natomiast zaimplementować interfejs za pomocą zwykłego modułu klasy. Ważne jest wykorzysta- nie w tej sytuacji instrukcji Implements (która była dostępna również w VB 6, jednak mogła odnosić się jedynie do interfejsów zewnętrznych): Public Class CRectangle ' Implementacja interfejsu IShape Implements IShape Public Overridable Sub Draw() Implements IShape.Draw ' kod metody End Sub Public Overridable Sub Spin(ByVal sngDegrees As Single) Implements IShape.Rotate ' kod metody End Sub ... End Class Należy zauważyć, że słowo kluczowe Implements występuje dodatkowo w każdej funkcji, która implementuje składową interfejsu. Zastosowanie tego słowa kluczowego umożliwia nadanie funkcji implementującej dowolnej nazwy. Funkcja implementująca nie musi mieć takiej samej nazwy jak metoda (metoda Spin implementuje metodę Rotate interfejsu IShape). Jednak nadawanie tej samej nazwy obu funkcjom jest niewątpliwie bardziej czytelne (a przez to prezentuje lepszy styl programowania.) Główną korzyścią wynikającą ze stosowania słowa kluczowego Implements do defi- niowania interfejsu jest fakt, że pojedyncza klasa może w ten sposób implementować wiele interfejsów. Dzieje się tak, pomimo że VB .NET nie zezwala na dziedziczenie w ob- rębie jednej klasy z wielu klas bazowych. Z drugiej strony — stosowanie słowa kluczo- wego Interface wiąże się z brakiem możliwości podania implementacji w module definiującym ten interfejs. W ten sposób wszystkie składowe muszą zostać zaimplemento- wane w każdej klasie implementującej dany interfejs. Oznacza to powtarzanie tego samego kodu w przypadku, gdy składowa interfejsu ma taką samą implementację w więcej niż jednej implementującej klasie.
  • 26. Polimorfizm i przeciążanie 87 Polimorfizm i przeciążanie Na szczęście nie musimy wnikać w szczegóły leżące u podstaw mechanizmu polimorfizmu i przeciążania z całą ich zawiłością i dwuznacznością. Niektórzy informatycy na przy- kład mówią, że przeciążanie jest formą polimorfizmu, inni natomiast twierdzą, że nie jest tak. Omówimy jedynie te zagadnienia, które wiążą się bezpośrednio z .NET Framework. Przeciążanie Terminu przeciążanie używamy w odniesieniu do takich elementów języka, które mogą zostać użyte w więcej niż jeden sposób. Nazwy operatorów są często przeciążane. Znak plus (+) na przykład może określać dodawanie liczb całkowitych, dodawanie liczb typu Single, dodawanie liczb Double czy łączenie łańcuchów. W ten sposób symbol plus (+) jest przeciążany. Ma to swoje dobre strony, gdyż w przeciwnym przypadku musieli- byśmy używać oddzielnych symboli do dodawania liczb całkowitych, liczb typu Single czy typu Double. Nazwy funkcji są również przeciążane. Funkcja Abs zwracająca wartość bezwzględną liczby może pobierać jako parametr liczbę całkowitą, liczbę typu Single lub typu Double. Ponieważ nazwa Abs reprezentuje klika różnych funkcji, to znaczy to, że jest nazwą przeciążoną. Rzeczywiście tak jest — przeglądając dokumentację składowej Abs klasy Math (w systemowej przestrzeni nazw biblioteki klas Base) znajdujemy następu- jące deklaracje funkcji o nazwie Abs: Overloads Public Shared Function Abs(Decimal) As Decimal Overloads Public Shared Function Abs(Double) As Double Overloads Public Shared Function Abs(Short) As Short Overloads Public Shared Function Abs(Integer) As Integer Overloads Public Shared Function Abs(Long) As Long Overloads Public Shared Function Abs(SByte) As SByte Overloads Public Shared Function Abs(Single) As Single Słowo kluczowe Overloads oznacza, że funkcja jest przeciążona. Nazwa funkcji jest przeciążona, jeżeli dwie zdefiniowane funkcje mają tę samą nazwę, ale różne sygnatury argumentów. Weźmy jako przykład funkcję zwracającą bieżące saldo rachunku. Rachunek może być identyfikowany przez nazwę osoby albo przez numer rachunku. W ten sposób możemy zdefiniować dwie funkcje i obydwie nazwać GetBalance: Overloads Function GetBalance(sCustName As String) As Decimal Overloads Function GetBalance(sAccountNumber As Long) As Decimal Należy zauważyć, że przeciążanie funkcji w VB .NET możliwe jest dlatego, że sygnatury ar- gumentów dwu funkcji są różne. W ten sposób nie ma dwuznaczności. Wywołania funkcji: getBalance("John Smith") getBalance(123456)
  • 27. 88 Rozdział 3. Wprowadzenie do programowania obiektowego są interpretowane przez kompilator bez trudności na podstawie typu danych argumentu. Ten rodzaj przeciążania jest często określany mianem przeciążania funkcji GetBalance. Przedstawione powyżej funkcje są jednak dwiema różnymi funkcjami, bardziej poprawne jest więc stwierdzenie, że przeciążana jest nazwa funkcji. Przeciążanie jest powszechnie stosowaną techniką programistyczną, której zastosowanie wykracza daleko poza pro- gramowanie obiektowe. Polimorfizm Termin polimorfizm oznacza występowanie w wielu różnych postaciach. W .NET Fra- mework polimorfizm jest ściśle związany z dziedziczeniem. Ponownie wróćmy do przy- kładu klasy opisującej pracownika przedsiębiorstwa. Funkcja IncSalary została zdefi- niowana w trzech klasach: klasie bazowej CEmployee oraz jej klasach potomnych CExecutive i CSecretary. W ten sposób funkcja IncSalary występuje w trzech posta- ciach. To jest właśnie polimorfizm w stylu VB .NET. Osoby zainteresowane omawianym zagadnieniem należy poinformować, że wielu infor- matyków nie uznaje przedstawionego powyżej mechanizmu za polimorfizm. W tym miej- scu powiedzieliby, że funkcja IncSalary przyjmuje tylko jedną postać. Różne są jedynie implementacje, a nie funkcja. Opisaną sytuację określiliby mianem przeciążenia funkcji IncSalary. Problemem występującym w tym przypadku jest zamieszanie w sposobie, w jaki Microsoft oraz pozostałe osoby używają terminów przeciążenie i polimorfizm — dlatego należy być ostrożnym podczas czytania dokumentacji. Zasięg i dostęp w module klasy Pojęcie zasięgu w module klasy jest znacznie ważniejsze niż w zwykłych modułach. Nie ma większych różnic w przypadku rozpatrywania zmiennych lokalnych (o zasięgu ograniczonym do bloku lub procedury) — tak samo w module klasy mamy zmienne o zasięgu ograniczonym do bloku oraz zmienne ograniczone do procedury. Natomiast zmiennym zadeklarowanym części Declarations modułu klasy może być przypisany jeden z następujących modyfikatorów dostępu: • Public; • Private; • Friend; • Protected; • Protected Friend. W przypadku zwykłych modułów dozwolone są jedynie Public, Private i Friend.
  • 28. Zasięg i dostęp w module klasy 89 Sam moduł klasy może być zadeklarowany z jednym z trzech modyfikatorów dostępu: Public, Private lub Friend (Protected nie jest dozwolony). Podanie w deklaracji modułu klasy jednego z tych modyfikatorów dostępu po prostu ogranicza dostęp do wszystkich składowych modułu do tego poziomu dostępu. Podany zasięg może zostać dalej ograniczony dla konkretnej składowej poprzez umieszczenie modyfikatora dostępu w deklaracji tej składowej. W ten sposób w przypadku dostępu do klasy typu Friend żadna z jej składowych nie może mieć dostępu typu Public (innymi słowy — dostęp typu Public zostaje przesłonięty dostępem typu Friend). Natomiast wszystkie cztery modyfikatory dostępu mają wpływ na składowe modułu klasy — to znaczy na deklaracje zmiennych, stałych, wyliczeń oraz procedur wewnątrz modułu klasy. Problem wynika z powodu istnienia trzech typów dostępu do składowej klasy o różnym zasięgu. W celu wyjaśnienia zdefiniujmy następujące składowe — przedstawione deklaracje nie są być może standardowe, ale z pewnością dobrze opisują zagadnienie. Przeanalizujmy następującą deklarację zmiennej w części Declarations modułu klas o nazwie Class1: AccessModifier classvariable As Integer Dostęp do tej zmiennej jest możliwy w przedstawiony niżej sposób. Bezpośredni dostęp Określa dostęp do składowej bez żadnych dodatkowych określeń: classvariable = 100 Podczas dostępu do zmiennej w bezpośredni sposób (tzn. bez dodatkowych określeń), zasięg zmiennej może obejmować: • klasę deklarującą; • klasę deklarującą i jej klasy pochodne (jedynie wewnątrz projektu, w którym ma miejsce deklaracja); • klasę deklarującą i jej klasy pochodne (w każdym projekcie zawierającym odwołanie do projektu, w którym ma miejsce deklaracja). Dostęp klasowy/obiektowy Określa dostęp do składowej za pomocą określenia w postaci nazwy klasy lub nazwy obiektu tej klasy. Stwierdziliśmy już, że w przypadku zadeklarowania zmiennej za pomocą słowa kluczowego Shared, jest ona wspólna dla wszystkich obiektów klasy. Wyrażając się ściślej można powiedzieć, że taka składowa istnieje niezależnie od jakiegokolwiek obiektu klasy. W tym przypadku dostęp do składowej (wewnątrz zasięgu składowej) jest możliwy przez podanie określenia w postaci nazwy klasy: Class1.classvariable = 100 Należy zauważyć, że dostęp do takiej składowej jest możliwy również poprzez podanie nazwy obiektu. Rezultat jest taki sam jak w przypadku podania nazwy klasy — istnieje jedynie jedna kopia składowej.
  • 29. 90 Rozdział 3. Wprowadzenie do programowania obiektowego Jeżeli składowa jest zadeklarowana bez użycia słowa Shared, wtedy dostęp możliwy jest przez podanie nazwy istniejącego obiektu: Dim c As new Class1 c.classvariable = 100 Zasięg klasowy/obiektowy może obejmować: • deklarującą klasę; • deklarujący projekt; • deklarujący projekt i te komponenty zewnętrzne, które mają odwołanie do deklarującego projektu. Tabela 3.2 opisuje różne modyfikatory dostępu. Tabela 3.2. Modyfikatory dostępu w module klasy Zasięg przy dostępie bezpośrednim Zasięg klasy/obiektu Private Klasa deklarująca Klasa deklarująca Protected Wszystkie pochodne klasy Klasa deklarująca Friend Klasy pochodne w projekcie Deklarowany projekt Protected Friend Wszystkie pochodne klasy Deklarowany projekt Public Wszystkie pochodne klasy Wszystkie projekty Nie było możliwe niezależne przedstawienie wpływu modyfikatorów Friend i Protec- ted. Lepszym rozwiązaniem byłoby zapewne oddzielne przedstawienie modyfikatorów dostępu przy bezpośrednim i klasowym lub obiektowym dostępie, zamiast powyższego przeplatania pojęć w tabeli 3.2. No cóż...