Programowanie obiektowe w VB



Podobne dokumenty
Programowanie obiektowe w VB cz 2

Programowanie obiektowe. Obiekt Klasa Składnia klasy: Interfejsy Składnia interfejsu: Metody Składnia instrukcji Sub: Składnia instrukcji function:

.NET Klasy, obiekty. ciąg dalszy

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Dziedziczenie. Tomasz Borzyszkowski

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Materiały do zajęć VII

Dziedziczenie. dr Jarosław Skaruz

Wykład 8: klasy cz. 4

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

Kurs WWW. Paweł Rajba.

TEMAT : KLASY DZIEDZICZENIE

PHP 5 język obiektowy

Wykład 5 Okna MDI i SDI, dziedziczenie

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Programowanie obiektowe

Programowanie obiektowe

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Typy klasowe (klasy) 1. Programowanie obiektowe. 2. Założenia paradygmatu obiektowego:

Klasa bazowa i klasy potomne - doskonalenie umiejtnoci projektowania i wykorzystania klas (45 min)

Narzędzia 2. dr inż. Tadeusz Jeleniewski

Programowanie obiektowe

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Klasy cd. Struktury Interfejsy Wyjątki

Wykład 4: Klasy i Metody

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

Polimorfizm. dr Jarosław Skaruz

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Co to jest klasa? Z programistycznego punktu widzenia klasa stanowi typ danych, który odwzorowuje wspólne cechy jakiegoś obiektu.

Języki i techniki programowania Ćwiczenia 2

Wykład 5: Klasy cz. 3

Klasy abstrakcyjne i interfejsy

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Definicje klas i obiektów. Tomasz Borzyszkowski

Programowanie Obiektowe w Visual Basic.NET Konrad Lipiński

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Aplikacje w środowisku Java

Podstawy programowania III

Technologie i usługi internetowe cz. 2

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Szablony klas, zastosowanie szablonów w programach

Programowanie obiektowe

10. Programowanie obiektowe w PHP5

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Programowanie Komputerów

Oracle PL/SQL. Paweł Rajba.

Rozdział 4 KLASY, OBIEKTY, METODY

Programowanie obiektowe i zdarzeniowe

Programowanie obiektowe

> C++ dziedziczenie. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

Ada-95. Dariusz Wawrzyniak

Zaawansowane programowanie w języku C++ Klasy w C++

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Enkapsulacja, dziedziczenie, polimorfizm

Podstawy Programowania Obiektowego

1. CZYM JEST SERIALIZACJA

Projektowanie obiektowe. Roman Simiński Wzorce projektowe Wybrane wzorce strukturalne

Tworzenie własnych komponentów

Platformy Programistyczne Podstawy języka Java

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Aplikacje w środowisku Java

Dokumentacja do API Javy.

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Definiowanie własnych klas

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

KOTLIN. Język programowania dla Androida

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

C# 6.0 : kompletny przewodnik dla praktyków / Mark Michaelis, Eric Lippert. Gliwice, cop Spis treści

Podstawy Języka Java

Podstawy Programowania semestr drugi. Wykład czternasty

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

Programowanie obiektowe

Programowanie, część I

Programowanie, część I

Podstawy programowania obiektowego

Programowanie obiektowe - 1.

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Programowanie obiektowe

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Klasy abstrakcyjne, interfejsy i polimorfizm

Język JAVA podstawy. Wykład 4, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Transkrypt:

Programowanie obiektowe w VB W VB 6.0 słowo kluczowe Set używane było do instrukcji przypisania referencji. Set używano w przypisaniach wartości dla domyślnych własności klas. Np. kontrolka Label ma własność domyślną Caption. Jeśli więc ktoś ustawiał jakąś zmienną typu Label na inną bez użycia Set, to wynikiem było skopiowanie własności Caption z jednej kontrolki do drugiej. Użycie Set powodowało zaś ustawienie tej samej referencji w Caption w drugiej kontrolce. W VB.NET słowa kluczowego Set w przypisaniach już się nie używa. Klasy Inheritance Polymorphism Encapsulation W.NET bazą dla wszystkich klas jest klasa System.Object (nie trzeba daklarować dziedziczenia po tej klasie). Klasa ta dysponuje następującymi metodami: Equals( ) sprawdza, czy dwa obiekty są równoważne Finalize( ) służy do zwalniania zasobów, jest wywoływana przez destruktor GetHashCode( ) pozwala dostarczyć obiektom ich własną funkcje haszującą do użycia w kolekcjach GetType( ) dostarcza dostęp do typu obiektu MemberwiseClone( ) tworzy kopie obiektu, nigdy nie powinna być implementowana ReferenceEquals( ) ocenia, czy dwa obiekty referują tą samą instancję ToString( ) dostarcza ciągu znaków reprezentującego obiekt Do definiowania klas służy wyrażenie Class. Pozwala ono określić nazwę klasy, charakter dostępu, dziedziczenie (klasę bazową i implementowane interfejsy), elementy klasy (zmiennych, właściwości, zdarzeń, metod). Składnia wyrażenia Class jest następująca: [ <attrlist> ] [ Public Private Protected Friend Protected Friend ] [ Shadows ] [ MustInherit NotInheritable ] _ Class name [ Inherits classname ] [ Implements interfacenames ] [ statements ] attrlist - opcjonalne. Lista atrybutów, które stosuje się do tej klasy. Jesli atrybutów jest wiele, oddzielane są one przecinkami. Public Private Protected Friend Protected Friend opcjonalne modyfikatory dostępu. Public dostęp publiczny (nie ma żadnych ograniczeń w używaniu bytów tego typu) Private dostęp prywatny (dostęp do bytów tego typu możliwy jest tylko w obszarze ich deklaracji, włączając w to byty zagnieżdżone; inaczej mówiąc jeśli coś jest prywatne, nie można się do tego odwołać spoza bloku, w którym to coś jest zadeklarowane)

Protected dostęp chroniony (takie byty są dostępne z poziomu własnej klasy lub klasy potomnej). Dostęp chroniony może być tylko zadeklarowany dla członków klasy. Dostęp ten nie jest nadzbiorem dostępu typu friend. Friend dostęp przyjazny (byty z dostępem przyjaznym osiągalne są tylko z poziomu programy, który zawiera ich deklaracje). Jest do dostęp przyjmowany domyślnie, gdy w deklaracji klasy nie użyto modyfikatora dostępu. Protected Friend dostęp będący unią dostępu chronionego i przyjaznego. Shadows opcjonalne. Wskazuje, że ta klasa zasłania (zacienia) identycznie nazwany element programowania w klasie bazowej. Można zasłaniać rodzaj zadeklarowanego elementu dowolnym (innym) rodzajem. Element zasłonięty jest nieosiągalny z poziomu klasy go zasłaniającej, chyba że element zasłonięty jest niedostępny (np. jest typu Private). MustInherit opcjonalne. Wskazuje, że członkowie klasy, którzy nie są współdzieleni (shared) mogą być osiągalni tylko poprzez klasę potomną. Inaczej mówiąc, klasa taka jest klasą abstrakcyjną, nie można tworzyć jej instancji. NotInheritable opcjonalne. Mówi o tym, że klasa ta nie może być klasą bazową innych klas (tj. nie można z niej dziedziczyć). name wymagana nazwa klasy. Inherits opcjonalne. Służy do określenia klasy bazowej bieżącej klasy. Uwaga: w VB istnieje dziedziczenie jednokrotne, tj. dana klasa może mieć tylko jedną klasę bazową (jednego rodzica, po którym dziedziczy). classname nazwa klasy bazowej (jeśli mamy dziedziczenie). Implements opcjonalne. Pozwala określić interfejs, jaki implementuje bieżąca klasa (może to być również lista interfejsów). Jeśli używa się tego wyrażenia, to musi ono występować zaraz za wyrażeniem Inherits (jeśli takowe jest), które występuje bezpośrednio za wyrażeniem Class. Jeśli bieżąca klasa nie implementuje w pełni zadeklarowanego interfejsu, klasa taka będzie klasą abstrakcyjną (a więc będzie musiało wystąpić w jej deklaracji MustInherit). interfacenames wymagana lista nazw implementowanych przez klasę interfejsów jeśli użyte zostało wyrażenie Implements. statements opcjonalne. Jest to część implementacyjna klasy, zawierająca zmienne, właściwości, zdarzenia, metody, zagnieżdżone klasy. koniec bloku definiującego klasę. Każdy atrybut na w części attrlist ma następującą składnię oraz części:: attrname [({ attrargs attrinit })] attrname wymagana nazwa atrybutu attrargs opcjonalna lista argumentów pozycyjnych dla tego atrybutu. Wielokrotne argumenty oddzielane są przecinkami. attrinit opcjonalna lista pól lub inicjalizatorów właściwości dla tego atrybutu. Wielokrotne inicjalizatory oddzielane są przecinkami. Uwagi: Klasy, które nie posiadają wyspecyfikowanego jawnie modyfikatora dostępu domyślnie traktowane są jako klasy z modyfikatorem Friend.

Wewnątrz bloku definicji klasy członkowie klasy mogą być deklarowani jako: Public, Private, Protected, Friend, lub Protected Friend. Wszystko zadeklarowane wewnątrz klasy jako Public jest widoczne zarówno wewnątrz jak i na zewnątrz klasy. Wszystko, co nie jest jawnie zadeklarowane z jakimś modyfikatorem dostępu domyślnie traktowane jest jako Public, za wyjątkiem pól i stałych, których domyślnym modyfikatore dostępu jest Private. Zmienne publiczne (nazywane czasem polami -Fields), służą jako właściwości klasy, jak właściwości zadeklarowanych poprzez wyrażenie Property. Domyślne właściwości i metody klasy określa się przez użycie słowa kluczowego Default w ich deklaracjach. W przypadku użycia nazw niekwalifikowanych w klasach zagnieżdżonych, odpowiednie elementy klasy zaczną być poszukiwane w klasie bieżącej, następnie w klasie zawierającej klasę bieżącą, itd. aż do klasy najbardziej zewnętrznej. Prywatni członkowie klasy zewnętrznej mogą być referowani, ale zgłoszony będzie błąd dla referencji do członków instancji klas zawierających. Klasy zagnieżdżone nie mogą dziedziczyć z klas, w których je zagnieżdżono. Przykład: Definicja klasy o nazwie MojaKlasa Public Class MojaKlasa 'miejsce na deklarację zmiennych, właściwości, metod, zdarzeń Kiedy deklaruje się jakąś klasę, która dziedziczy po innej klasie często mówi się, że klasa bazowa jest generalizacją, zaś klasa pochodna specjalizacją. Przykład specjalizacji: Public Class ListBox Inherits Window lub w równoważnym zapisie: Public Class ListBox : Inherits Window Konstruktory Konstruktory są to metody klasy, które wywoływane są podczas tworzenia obiektów. Konstruktory mogą być przeciążane. Domyślny konstruktor nie pobiera żadnych parametrów. Jeśli w klasie nie jest zaimplementowany żaden konstruktor, kompilator utworzy konstruktor domyślny z pustą implementację. Jeśli jest zaimplementowany jakikolwiek konstruktor, kompilator już domyślnego konstruktora nie utworzy. Wtedy dobrze jest dostarczyć taki konstruktor samemu. Public Sub New() NumDoors = 4 NumWheelsValue = 4

Public Sub New(ByVal Doors As Integer, ByVal Wheels As Integer) NumDoors = Doors NumWheelsValue = Wheels Function testvehicle() Dim clsvehicle As New Vehicle(4, 4) clsvehicle.numdoors = 4 End Function Public Sub New(Optional ByVal Doors As Integer = 4, Optional ByVal Wheels As Integer = 4) NumDoors = Doors NumWheelsValue = Wheels Jeśli w klasie potomnej wywoływany jest konstruktor klasy nadrzędnej, to jego wywołanie musi pojawić się w pierwszej linijce implementowanego konstruktora (w pierwszej linijce wywoływane są bezparametrowe konstruktory domyślne jeśli nie zdefiniowano żadnych konstruktorów). Przy wywołaniach konstruktorów klas nadrzędnych korzysta się ze słowa kluczowego MyBase. Przykład konstruktora z wywołaniem konstruktora klasy nadrzędnej: Public Sub New( _ ByVal top As Integer, _ ByVal left As Integer, _ ByVal thecontents As String) MyBase.New(top, left) ' call base constructor mlistboxcontents = thecontents 'New Konstruktory kopiujące. Aby utworzyć kopię jakiegoś obiektu, można posłużyć się konstruktorem kopiującym. Konstruktor taki powinien stworzyć nowy obiekt, kopiując do niego wszystkie zmienne z istniejącego obiektu tego samego typu. VB.NET nie dostarcza konstruktorów kopiujących. Jeśli więc istnieje konieczność ich użycia, należy je samemu zaimplementować. Przykład konstruktora kopiującego podany jest poniżej: Public Sub New(ByVal existingobject As Time) year = existingobject.year month = existingobject.month date = existingobject.date hour = existingobject.hour minute = existingobject.minute second = existingobject.second Punkt wejściowy aplikacji Ponieważ w programowaniu obiektowym implementuje się klasy obiektów, zaś same obiekty tworzy są wewnątrz metod klas, powstaje pytanie, jak zainicjalizować powstanie jakiegokolwiek obiektu, nie mówiąc już u wykonywaniu jego metod. Otóż działanie programów w VB.NET rozpoczyna się od wywołania metody Main() jakiejś klasy przez system operacyjny. Klasę, której metoda zostanie wykonana można określić w środowisku programowania. Zazwyczaj jest to bardzo mała klasa, która zawiera tylko metodę Main() (lub moduł który i tak kompilowany jest do postaci klasy).

Przykład modułu z metodą Main() Module modmain Public Sub Main()... End Module Przykład klasy z metodą Main(): Public Class Tester Public Sub Main() Dim testobject As New Tester ' other members Przeciążanie (Overloading) Przeciążanie ma miejsce, gdy istnieją przynajmniej dwie metody o tej samej nazwie i o różnych parametrach (tj. listy parametrów różnią się długością i/lub typem parametrów). Metody przeciążalne deklaruje się ze słowem kluczowym Overloads. Public Overloads Function Add(ByVal x As Integer, ByVal y As Integer) Return x + y End Function Public Overloads Function Add(ByVal x As Double, ByVal y As Double) Return x + y End Function clsvehicle.add(1, 1) 'calls first Add function clsvehicle.add(1.5, 1.5) 'calls second Add function clsvehicle.add(1, 1.5) 'calls second Add function Klasa może posiadać dowolną liczbę metod, pod warunkiem, że metody te różnią się pomiędzy sobą sygnaturami (tj. nazwami i/lub liczbą i typem argumentów). Zadeklarowanie metod o tych samych sygnaturach, lecz różnym typie wartości zwracanej traktowane jest jako błąd (nie można w ten sposób przeciążać metod). Przesłanianie (Overriding) Przesłanianie ma miejsce, gdy w drzewie dziedziczenia znajdą się klasy posiadające metod o takich samych sygnaturach. Aby podczas dynamicznych wywołań wykonała się właściwa danemu obiektowi metoda, należy posłużyć się słowami kluczowymi Overridable oraz Overridden. Class Square Public Overridable Function getcircumference(byval r As Double) As Double Return (2 * r) * 4 'length of side time 4 sides End Function Function testsquare() Dim clssquare As New Square

Dim circ As Double circ = clssquare.getcircumference(1) End Function Class Circle Inherits Square Public Overrides Function getcircumference(byval r As Double) As Double Return 2 * 3.14 * r End Function Public Function testcircle() Dim clscircle As New Circle Dim clssquare As New Square Dim circsquare As Double Dim circcircle As Double circcircle = clscircle.getcircumference(1) 'returns 6.28 circsquare = clssquare.getcircumference(1) 'returns 8 End Function Podczas implementacji danej klasy definiuje się cechy i właściwości obiektów, które będą tworzone podczas wykonywania programu (miejsce tworzenia obiektów w kodzie źródłowym programu rozpoznać można po wystąpieniu konstruktora New). Aby odwołać się do instancji klasy wewnątrz implementacji tej klasy (tj. aby odwołać się do czegoś, co dopiero powstanie), używa się słowa kluczowego Me. Me zachowuje się jak referencja do obiektu (lub zmienna typu strukturalnego, gdy Me użyte jest w implementacji struktury). Me szczególnie użyteczne jest do przekazywania informacji o bieżącej instancji klasy (obiekcie) lub strukturze do procedury innej klasy, struktury lub modułu. Public Sub mymethod() Dim someobject As New SomeType someobject.somemethod(me) Innym częstym zastosowaniem Me jest rozróżnienie parametrów metod od zmiennych instancyjnych Public Sub SomeMethod(ByVal Hour As Integer) Me.Hour = Hour Aby odwołać się do klasy nadrzędnej i jej metody z poziomu jakiejś klasy należy skorzystać ze słowa kluczowego MyBase. Public Function getcircumference(byval r As Double) As Double Return MyBase.getCircumference(r) End Function Podobne znaczenie ma słowo kluczowe MyClass. Odnosi się jednak ono do instancji bieżącej klasy, a nie klasy. W tym sensie MyClass podobne jest do Me. Jednak z użyciem MyClass wiąże się inna interpretacja wywoływanych metod. Metody wywoływane za pomocą MyClass traktowane są jakby były NotOverridable. Dlatego jeśli jakaś zmienna typu A przechowująca referencję do obiektu typu B posiada metodę, w której jest odwołanie do MyClass, to MyClass będzie dotyczyło klasy A, a nie klasy B.

Class BaseClass Public Overridable Sub MyMethod() MsgBox("Base class string") Public Sub UseMe() Me.MyMethod() ' Use calling class's version, even if an override. Public Sub UseMyClass() MyClass.MyMethod() ' Use this version and not any override. Class DerivedClass : Inherits BaseClass Public Overrides Sub MyMethod() MsgBox("Derived class string") Class TestClasses Sub StartHere() Dim TestObj As DerivedClass = New DerivedClass TestObj.UseMe() ' Displays "Derived class string". TestObj.UseMyClass() ' Displays "Base class string". W powyższym przykładzie, pomimo, że klasa DerivedClass nadpisywała metodę MyMethod, użycie MyClass w metodzie UseMyClass kasowało efekt nadpisania. Stąd kompilator wywołał metodę MyMethod klasy bazowej. MyClass nie może być użyte wewnątrz metod współdzielonych (tj. metod zadeklarowanych jako Shared), jednak można użyć tego słowa wewnątrz metod instancyjnych, aby uzyskać dostęp do współdzielonych członków klasy. Słowo kluczowe NotOverridable służy do deklaracji metod, które nie mogą zostać przysłonięte w klasach potomnych. Jeśli podczas deklaracji jakiejś metody nie użyje się ani Overridable ani NotOverridable, to taka metoda domyślnie potraktowana zostanie jako metoda, której przysłaniać nie można. NotOverridable i Overridable wzajemnie się wykluczają. Brak Overridable przy deklaracji metody znaczy, że jest to metoda NotOverridable. Aby wymusić nadpisanie jakiejś metody danej klasy w klasie potomnej należy użyć słowa kluczowego MustOverride. Jeśli jednak jakaś klasa zawiera metodę MustOverride, to klasa ta musi być oznaczona słowem kluczowym MustInherit: MustInherit Class Shape MustOverride Function getcircumference(byval r As Double) As Double Class Square Inherits Shape Public Overrides Function getcircumference(byval r As Double) As Double Return 2 * r * 4 End Function

MustInherit oraz MustOverride tworzą parę słów kluczowych niejako związanych ze sobą. Jeśli jakaś metoda zadeklarowana jako MustOverride, znaczy to, że w klasie bieżącej nie dostarcza się implementacji tej metody. I faktycznie, w powyższym przykładzie w klasie Shape mamy tylko sygnaturę funkcji (brak jest implementacji): MustOverride Function getcircumference(byval r As Double) As Double Metody, które nie posiadają implementacji nazywane są metodami abstrakcyjnymi. Jeśli jakaś klasa posiada metodę abstrakcyjną, to staje się ona również klasą abstrakcyjną klasą, której obiektów nie można tworzyć. Stąd, aby jawnie zaznaczyć, że mamy do czynienia z klasą abstrakcyjną używa się słowa kluczowego MustInherit. Po co więc stosuje się metody i klasy abstrakcyjne. Stosuje się je głownie po to, aby poprawniej tworzyć projekty programowe. Na etapie projektowania można założyć, jakimi metodami będą dysponować obiekty danych klas, dostarczając abstrakcyjną klasę bazową. Klasy pochodne (tj. specjalizacje, które w chwili projektowania jeszcze nie są dokładnie sprecyzowane) dostarczą implementacje metod abstrakcyjnych. Co więcej, chociaż nie można tworzyć obiektów klas abstrakcyjnych, to jednak same klasy abstrakcyjnych mogą być użyte w kodzie źródłowym programu. Korzysta się z nich w ten sposób, że definiuje się zmienne typu klas abstrakcyjnych w celu przechowywania w nich referencji do instancji klas dziedziczących po tych klasach. Jedną z ciekawych metod (z klasy bazowej Syste.Object) jest metoda nadpisywalna ToString( ). Metoda ta wywoływana jest automatycznie w miejscach, w których oczekuje się ciągu znaków, zaś wstawiona jest w to miejsce referencja do obiektu. Metodę tą zazwyczaj nadpisuje się w własnej klasie wg następującego schematu: Public Overrides Function ToString() As String Return parametr.tostring() End Function Uwagi na temat Overridable i Overrides: Deklaracja metod, które mogą być nadpisywane i metod je nadpisujących w VB.NET musi odbywać się jawnie. Służą do tego słowa kluczowe Overridable oraz Overrides. Ich znaczenie objaśniją poniższe rozważania. Przypuśćmy, że istnieje klasa bazowa o nazwie Window, której autorem jest programista A. Przypuśćmy, że klasa ListBox została napisane przez programistę B, który użył klasy Window jako klasy bazowej własnych klas. Programista B właściwie nie ma wpływu na sposób implementacji klasy Window. Co więcej, nie musi nawet znać szczegółów jej implementacji ani kierunków, w jakich klasa Window będzie się rozwijać w przyszłych implementacjach przygotowywanych przez programistę A. Przypuśćmy teraz, że programista B dodaje metodę Sort( ) do klasy ListBox: Public Class ListBox Inherits Window Public Overridable Sub Sort() '... Dodanie nowej metody nie stwarza żadnych problemów dopóki programista A nie zmodyfikuje klasy Window swojego autorstwa. Jeśli bowiem programista A opracuje drugą implementację klasy Window, w której pojawi się metoda Sort( ): Public Class Window Public Overridable Sub Sort() '...

to powstaje problem. W innych językach obiektowego programowania (np. C++), nowa, nadpisywalna metoda Sort( ) w klasie Window mogłaby być traktowana jako metoda bazowa względem metody nadpisywalnej Sort( ) w klasie ListBox. Podczas kompilacji programów kompilator mógłby tworzyć odwołania do Sort( ) z ListBox, kiedy faktycznie celem programisty byłoby wywołanie Sort( ) z klasy Window. W języku Java, jeśli Sort( ) w klasie Window ma inny typ wartości zwracanej, to ładowacz klas mże potraktować Sort( ) w ListBox jako niewłaściwą metodę nadpisującą zgłaszając błąd podczas jej ładowania. W VB.NET metoda nadpisywalna zawsze jest rozważana jako miejsce rozgałęzienie, tzn. jeśli VB.NET znajdzie metodę nadpisywalną, to nie szuka on wzwyż drzewa dziedziczenia żadnych innych nadpisywalnych metod. Jeśli nowa nadpisywalna metoda Sort( ) jest wprowadzona w klasie Window, zachowanie klasy ListBox podczas wykonywania programu nie zmieni się. Jednak podwójne Overridable w drzewie dziedziczenia jest dostrzegane przez kompilator, który w czasie kompilacji ListBox generuje ostrzeżenie: Module1.vb(31) : warning BC40005: sub 'Sort' shadows an overridable method in a base class. To override the base method, this method must be declared 'Overrides'. Aby zlikwidować to ostrzeżenie, programista musi zaznaczyć jawnie, o co mu chodzi. Może on zaznaczyć, że Sort( ) w ListBox przysłania Sort() z klasy nadrzędnej przez użycie słowa kluczowego Shadows. Użycie Shadows mówi, że Sort() z ListBoc nie jest nadpisaniem metody nadpisywalnej z klasy Window, a czymś zupełnie nowym: Public Class ListBox Inherits Window Public Shadows Sub Sort() '... 'Sort Jeśli programista chciałby przysłonić metodę z klasy nadrzędnej, musiałby przy metodzie Sort() w klasie ListBox użyć słowa kluczowego Overrides: Public Class ListBox Inherits Window Public Overrides Sub Sort() '... 'Sort Współdzieleni członkowie klas (Shared Members) Słowa kluczowego Shared używa się do deklaracji takich elementów klasy, które współdzielone będą przez wszystkie obiekty tej klasy. Class SomeClass Private Shared NumInstances As Integer = 0

Public Sub New() NumInstances = NumInstances + 1 Public ReadOnly Property Instances() As Integer Get Return NumInstances End Get End Property Public Sub testshared() Dim clssomeclass1 As New SomeClass Dim clssomeclass2 As SomeClass Dim num As Integer num = clssomeclass1.instances ' returns 1 clssomeclass2 = New SomeClass num = clssomeclass2.instances ' returns 2 Właściwości (Properties) Właściwości określa się słowem kluczowym Property. Właściwości mogą być do zapisu i odczytu, tylko do zapisu, tylko do odczytu. Odpowiednią cechę właściwości uzyskuje się przez zastosowanie (lub brak) słów kluczowych ReadOnly oraz WriteOnly. Składnia wyrażenia służącego definiowaniu właściwości klasy jest następująca: [ <attrlist> ] [ Default ] [ Public Private Protected Friend Protected Friend ] _ [[ ReadOnly WriteOnly ] [ Overloads Overrides ] _ [ Overridable NotOverridable ] MustOverride Shadows Shared ] _ Property varname([ ByVal parameter list ]) [ As typename ] [ Implements interfacemember ] [ <attrlist> ] Get [ block ] End Get [ <attrlist> ] Set(ByVal value As typename ) [ block ] End Set End Property Wiele elementów w składni Property pokrywa się z elementami ze składni Class. attrlist - opcjonalne. Lista atrybutów, które stosuje się do tej właściwości. Jeśli atrybutów jest wiele, oddzielane są one przecinkami. Public - opcjonalne. Służy do zadeklarowania domyślnej właściwości. Domyślne właściwości muszą akceptować parametry, a ich użycie (tj. ustawianie lub odczytanie wartości właściwości) może odbywać się bez jawnej specyfikacji nazwy właściwości. Overloads opcjonalne. Wskazuje, że ta właściwość przeciąża jedną lub więcej właściwości zadeklarowanych z taką samą nazwą w klasie bazowej. Lista argumentów w tej deklaracji musi

różnić się od listy argumentów wszystkich innych przeciążonych właściwości. Różnica polegać ma na zadeklarowaniu argumentów o innych typach i/lub w różnej ilości. Słowo Overloads nie musi być używane, gdy definiowane są przeciążone właściwości w tej samej klasie. Jeśli jednak Overloads zostanie użyte w jednej deklaracji, to musi być użyte również i w pozostałych deklaracjach właściwości przeciążonych. Overloads nie może wystąpić równocześnie z Shadows przy deklaracji tej samej właściwości. Overrides opcjonalne. Wskazuje, że właściwość nadpisuje identycznie nazwaną właściwość w klasie bazowej. Liczba oraz typ argumentów, jak również typ wartości zwracanej w takim przypadku muszą być takie same jak w klasie bazowej. Overridable opcjonalne. Wskazuje, że właściwość może być przeciążona przez właściwość o identycznej nazwie zadeklarowanej w klasie potomnej. NotOverridable opcjonalne. Wskazuje, ze właściwość nie może zostać przeciążona w klasie potomnej. Właściwości domyślnie są NotOverridable, jednak nie można użyć modyfikatora NotOverridable dla własności lub metod, które nie przeciążają następnego członka klasy. MustOverride opcjonalne. Wskazuje, że właściwość nie jest zaimplementowana w klasie, tzn. musi ona być zaimplementowana w klasie potomnej. Dopóki brak w klasie implementacji właściwości, dopóty nie można tworzyć obiektów tej klasy (taka klasa jest klasą abstrakcyjną). Shadows opcjonalne. Wskazuje, że ta właściwość zasłania (zacienia) identycznie nazwany element programowania, lub zbiór elementów, w klasie bazowej. Można zasłaniać dowolny rodzaj zadeklarowanego elementu dowolnym (innym) rodzajem. Jeśli zacieniana jest właściwość przez inną właściwość, argumenty i typ zwracany nie muszą się zgadzać z argumentami i typem zwracanym z klasy bazowej. Element zasłonięty jest nieosiągalny z poziomu klasy go zasłaniającej, chyba że element zasłaniający jest niedostępny (np. jest typu Private). Overloads i Shadows nie mogą wystąpić w deklaracji tej samej właściwości. Shared opcjonalne. Wskazuje, że właściwość jest współdzielona przez wszystkie obiekty danej klasy (tzn. nie jest to właściwość związana z instancją klasy lub struktury). Do właściwości współdzielonych odwoływać się można poprzez użycie nazwy klasy lub struktury lub też poprzez użycie nazwy instancji klasy lub struktury. Public Private Protected Friend Protected Friend opcjonalne modyfikatory dostępu. Public dostęp publiczny (nie ma żadnych ograniczeń w używaniu bytów tego typu). Właściwości bez modyfikatora dostępu traktowane są domyślnie jako Public. Private dostęp prywatny (dostęp do bytów tego typu możliwy jest tylko w obszarze ich deklaracji, włączając w to byty zagnieżdżone; inaczej mówiąc jeśli coś jest prywatne, nie można się do tego odwołać spoza bloku, w którym to coś jest zadeklarowane) Protected dostęp chroniony (takie byty są dostępne z poziomu własnej klasy lub klasy potomnej). Dostęp chroniony może być tylko zadeklarowany dla członków klasy. Dostęp ten nie jest nadzbiorem dostępu typu friend. Friend dostęp przyjazny. Elementy z dostępem przyjaznym osiągalne są tylko z poziomu programy, który zawiera ich deklaracje. Protected Friend dostęp będący unią dostępu chronionego i przyjaznego. ReadOnly opcjonalne. Wskazuje, że wartość właściwości może być odczytana, ale nie modyfikowana. Właściwości ReadOnly zawierają blok Get, nie posiadają zaś bloku Set. WriteOnly opcjonalne. Wskazuje, że wartość właściwości może być modyfikowana, ale nie może być odczytywana. Właściwości WritedOnly zawierają blok Set, nie posiadają zaś bloku Get. varname wymagana unikalna nazwa właściwości

parameter list opcjonalne. Identyfikuje sygnaturę właściwości. Parametry dla właściwości muszą być przekazywane przez wartość ByVal. typename opcjonalne. Jeśli nie wyspecyfikowano typu danych, domyślnym typem będzie Object. Jeśli właściwość implementuje właściwość w interfejsie, typename musi pokrywać się z typem zadeklarowanym w interfejsie. Implements opcjonalne. Wskazuje, że ta właściwość implementuje właściwość interfejsu. interfacemember opcjonalne. Gdy właściwość jest częścią klasy, która implementuje interfejs, jest to nazwa właściwości implementowanej. Get rozpoczyna procedurę Get właściwości. Procedura ta zwraca wartość właściwości. Procedury te są wymagane, jeśli właściwości nie są WriteOnly. End Get koniec bloku definiującego procedurę Get. Set rozpoczyna procedurę Set właściwości. Procedura ta jest używana do ustawiania wartości właściwości. Procedury Set są wymagane, jeśli właściwości nie są ReadOnly. Nowa wartość właściwości jest przekazywana procedurze Set jako parametr o nazwie value w chwili, gdy wartość właściwości jest zmieniana (w instrukcji przypisania). End Set koniec bloku definiującego procedurę Set. End Property koniec bloku definiującego właściwość. Każdy atrybut na w części attrlist ma następującą składnię oraz części:: attrname [({ attrargs attrinit })] attrname wymagana nazwa atrybutu attrargs opcjonalna lista argumentów pozycyjnych dla tego atrybutu. Wielokrotne argumenty oddzielane są przecinkami. attrinit opcjonalna lista pól lub inicjalizatorów właściwości dla tego atrybutu. Wielokrotne inicjalizatory oddzielane są przecinkami. Uwagi. VB.NET przekazuje parametry do bloku Set podczas wykonywania instrukcji przypisania. Jeśli Set nie zostanie zaimplementowane, to samo zintegrowane środowisko dostarczy parametr nazywany Value. Parametr zawiera zawartość elementu, który został przypisany do właściwości podczas wywołania bloku Set. Zawartość parametru zazwyczaj jest przechowywana w prywatnej, lokalnej zmiennej i jest zwracana, gdy wywołany jest blok Get. Przy braku ReadOnly i WriteOnly właściwość jest zarówno do odczytu, jak i do zapisu. W takim przypadku oba bloki Set... End Set oraz Get... End Get muszą być zaimplementowane. Class Vehicle Public NumDoors As Integer = 2 Private NumWheelsValue As Integer = 4 ' Used for property ops. Public Property NumWheels() As Integer ' This is a Property. Get Return NumWheelsValue End Get Set(ByVal Value As Integer) ' Only allow set operation for values less than 13. If Value < 13 Then NumWheelsValue = Value End If End Set

End Property Function testvehicle() Dim clsvehicle As New Vehicle Dim clsothervehicle As Vehicle clsvehicle.numdoors = 4 clsvehicle.numwheels = 4 clsothervehicle = New Vehicle clsothervehicle.numwheels = 13 End Function Przykład użycia właściwości typy ReadOnly. Class Vehicle Public NumDoors As Integer = 2 Private NumWheelsValue As Integer = 4 ' Used for property ops. Public ReadOnly Property NumWheels() As Integer Get Return NumWheelsValue End Get End Property Upraszczając powyższe, właściwości deklaruje się w następujący sposób: Property Identifier() As Type Get statements() End Get Set(ByVal Value As Type) statements() End Set End Property używa zaś w następujący sposób: Identifier =?????? = Identifier Stos i sterta a tworzenie zmiennych Wszyskie zmienne i obiekty, które tworzone są w programie muszą być umieszczone w jakimś obszarze pamięci. Obszary pamięci służące do ich przechowywania to stos i sterta. Obiekty, które tworzone są wewnątrz metod nazywane są zmiennymi lokalnymi. Zmienne lokalne istnieją tylko w obrębie bloku (metody), w którym zostały utworzone. Tzn. ich życie zaczyna się w chwili utworzenia, kończy zaś dojściem do końca metody. Zmienne lokalne typów podstawowych (jak Integer, Double) oraz strukturalnych tworzone są w obszarze pamięci nazywanym stosem. Odpowiedni obszar stosu jest rezerwowany (alokowany) w chwili wywołania metody. Gdy metoda się kończy, zwalniany jest stos (a wraz z nim niszczone są zmienne lokalne). Klasy są typami referencyjnymi. Zmienne typu referencyjnego przechowują referencje do obiektów utworzonych w obszarze pamięci nazywanym stertą. Dokładniej mówiąc: deklaracja zmiennej typu referencyjnego jest deklaracją zmiennej służącej do przechowywania referencji do jakiegoś obiektu. Każde wywołanie konstruktora New tworzy nowy obiekt w obszarze sterty. Oszar ten nie ma

faktycznie nazwy i aby się do niego jakoś odwołać należy użyć zmiennej referencyjnej. Skojarzenie zmiennej referencyjnej z istniejącym obiektem odbywa się już w deklaracji zmiennej referencyjnej: Dim milo As New Dog( ) albo dopiero w instrukcji przypisania Dim milo As Dog milo = New Dog() Zmienna referencyjna oraz obiekt z nią skojarzony nie są tym samym. Często jednak określenia zmienna referencyjna odnoszą się nie do samej zmiennej przechowującej referencję, a do obiektu z nią skojarzonego. Destrukcja obiektów Obiekty w VB.NET tworzone są przez jawne wywołanie odpowiednich konstruktorów. Ich niszczenie jednak nie odbywa się w sposób jawny. Autotorzy języka zwolnili programistów z obowiązku czyszczenia zajętej przez utworzone obiekty pamięci. Za to zadanie odpowiedzialny jest tzw. czyściciel (garbage collector). Chwila, w której usuwane są obiekty z pamięci nie jest ściśle określona. Obiekt trafia na listę obiektów do usunięcia, kiedy ginie ostatnia referencja (tj. kiedy podczas wykonywania programu blok w którym ostatnia zmienna referencyjna skojarzona z obiektem kończy się). Czyściciel usuwa taki obiekt w chwili, kiedy znajdzie na to czas. Mogą jednak w programie istnieć zasoby, które powinny być zwalniane w sposób jawny. Takimi zasobami (tzw. an unmanaged resource ) są zasoby spoza.net Framework, jak np. uchwyty plików, połączenia z bazami danych, itp. Aby zapewnić kontrolę nad takimi zasobami każda klasa.net wyposażona jest w metodę Finalize( ). Metoda ta wywoływana jest przez czyściciela w chwili, gdy obiekt danej klasy usuwany jest z pamięci. Protected Overrides Sub Finalize() ' release non-managed resources MyBase.Finalize() Metody Finalize( ) nie wywołuje się jawnie. Tylko czyściciel ma do tego prawo. Finalize() nazywana jest destruktorem. Aby udostępnić programistom większą kontrolę nad chwilą usuwania obiektów (i zamykania zasobów niezarządzanych w ramach.net Framework) w VB dostarczono interfejs IDisposable. Z użyciem tego interfejsu wiąże się zaimplementowanie metody Dispose( ), którą wywołuje się już jawnie. Sytuacja teraz się nieco komplikuje, bo wywołanie Dispose() wcale nie wyłącza wywołania metody Finalize(). Czyli jeśli ktoś wywoła Dispose() na rzecz jakiegoś obiektu, to może zwolnić zasoby, które również bedą usuwane w metodzie Finalize(). Aby rozwiązać ten problem w implementacji dostarcza się dodatkowej metody Dispose z parametrem typu Boolean. Jej wywołanie z wartością true znaczy, że metoda ta wywołana będzie przez użytkownika, zaś wywołanie z wartościa false będzie znaczyło, że metodę wywołał czyściciel. Można w ten sposób rozpoznać, w jakim trybie odbywa się zwalnianie obiektów i odpowiednio do tego zwalniać zasoby. W poniższym przykładzie programista dostarcza metodę Dispose( ), która uniemożliwia czyścicielowi wywołanie destruktora obiektu. Zastopowanie czyściciela odbywa się poprzez wywołanie metody współdzielonej GC.SuppressFinalize( ), do której przekazuje się referencję Me obiektu. Public Class Testing Implements IDisposable Dim is_disposed As Boolean = False

Protected Sub Dispose(ByVal disposing As Boolean) If Not is_disposed Then If disposing Then Console.WriteLine("Not in destructor, OK to reference other objects") End If ' perform cleanup for this object Console.WriteLine("Disposing...") End If Me.is_disposed = True Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) 'tell the GC not to finalize GC.SuppressFinalize(Me) Protected Overrides Sub Finalize() Dispose(False) Console.WriteLine("In destructor.") Jeśli w kodzie źródłowym mamy Sub Main() Dim t As Testing = New Testing to przy kończeniu procedury Main wywoła się destruktor obiektu t, a na ekranie zostanie wypisane: Disposing... In destruktor Jeśli w kodzie źródłowym mamy Sub Main() Dim t As Testing = New Testing t.dispose() Disposing... to jawne wywołanie Dispose zablokuje wywołanie destruktora obiektu t na końcu Main, a na ekranie pojawi się: Not in destructor, OK to reference other objects Disposing... Przykład z MSDN library: ' Design pattern for the base class. ' By implementing IDisposable, you are announcing that instances ' of this type allocate scarce resources. Public Class BaseResource Implements IDisposable ' Pointer to an external unmanaged resource. Private handle As IntPtr ' Other managed resource this class uses. Private Components As Component ' Track whether Dispose has been called.

Private disposed As Boolean = False ' Constructor for the BaseResource Object. Public Sub New() ' Insert appropriate constructor code here. ' Implement IDisposable. ' Do not make this method Overridable. ' A derived class should not be able to override this method. Public Overloads Sub Dispose() Implements IDisposable.Dispose Dispose(True) ' Take yourself off of the finalization queue ' to prevent finalization code for this object ' from executing a second time. GC.SuppressFinalize(Me) ' Dispose(disposing As Boolean) executes in two distinct scenarios. ' If disposing is true, the method has been called directly ' or indirectly by a user's code. Managed and unmanaged resources ' can be disposed. ' If disposing equals false, the method has been called by the runtime ' from inside the finalizer and you should not reference other ' objects. Only unmanaged resources can be disposed. Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) ' Check to see if Dispose has already been called. If Not (Me.disposed) Then ' If disposing equals true, dispose all managed ' and unmanaged resources. If (disposing) Then ' Dispose managed resources. Components.Dispose() End If ' Release unmanaged resources. If disposing is false, ' only the following code is executed. CloseHandle(handle) handle = IntPtr.Zero ' Note that this is not thread safe. ' Another thread could start disposing the object ' after the managed resources are disposed, ' but before the disposed flag is set to true. ' If thread safety is necessary, it must be ' implemented by the client. End If Me.disposed = True ' This Finalize method will run only if the ' Dispose method does not get called. ' By default, methods are NotOverridable. ' This prevents a derived class from overriding this method. Protected Overrides Sub Finalize() ' Do not re-create Dispose clean-up code here. ' Calling Dispose(false) is optimal in terms of ' readability and maintainability.

Dispose(False) ' Allow your Dispose method to be called multiple times, ' but throw an exception if the object has been disposed. ' Whenever you do something with this class, ' check to see if it has been disposed. Public Sub DoSomething() If Me.disposed Then Throw New ObjectDisposedException End If ' Design pattern for a derived class. ' Note that this derived class inherently implements the ' IDisposable interface because it is implemented in the base class. Public Class MyResourceWrapper Inherits BaseResource ' A managed resource that you add in this derived class. Private addedmanaged As ManagedResource ' A native unmanaged resource that you add in this derived class. Private addednative As NativeResource ' Track whether Dispose has been called. Private disposed As Boolean = False ' Constructor for the MyResourceWrapper object. Public Sub New() MyBase.New() ' Insert appropriate constructor code here for the ' added resources. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If Not (Me.disposed) Then Try If disposing Then ' Release the managed resources you added in ' this derived class here. addedmanaged.dispose() End If ' Release the native unmanaged resources you added ' in this derived class here. CloseHandle(addedNative) Me.disposed = True Finally ' Call Dispose on your base class. MyBase.Dispose(disposing) End Try End If ' This derived class does not have a Finalize method ' or a Dispose method without parameters because it ' inherits them from the base class.

Zasłanianie metod klasy bazowej (Shadowing) Użycie słowa kluczowego Shadows w deklaracjach metod klas potomnych powoduje, że metody klas bazowych zostają przysłonięte i w kolejnych pokoleniach klas to właśnie zasłaniające metody będą dziedziczone, a nie metody zasłaniane. Jedynym sposobem na wywołanie metod zasłoniętych jest skorzystanie ze słowa kluczowego MyBase. Opakowania Opakowania stosuje się po to, aby zmienne typu wartość przekształcić na zmienne typu referencyjnego (tj. do obiektów). Mówi się, że jakaś wartość jest opakowana obiektem (value boxed inside an Object), zaś wartość rozpakowana to wartość wyłuskana z obiektu (unboxed back to a value type). Opakowanie obiektów typu wartość typem Object jest opakowaniem implicite. Podczas opakowywania tego typu tworzony jest instancja klasy Object, do której kopiowana jest opakowywana wartość. Oczywiście można opakowanie wyrazić explicite dokonując rzutowania typów: Dim myintegervalue As Integer = 5 Dim myobject As Object = myintegervalue ' explicitly cast to object myobject.tostring( ) choć dla zmiennych typu wartość nie jest to konieczne: Dim myintegervalue As Integer = 5 myintegervalue.tostring( ) ' implicitly cast to object Rozpakowywanie obiektów, w przeciwieństwie do opakowywania, musi odbywać się explicite (jeśli jest włączona opcja Option Strict On: Option Strict On Imports System Public Class UnboxingTest Public Shared Sub Main() Dim myintegervariable As Integer = 123 ' boxing Dim myobjectvariable As Object = myintegervariable Console.WriteLine("myObjectVariable: {0}", _ myobjectvariable.tostring()) ' unboxing (must be explicit) Dim anotherintegervariable As Integer = _ DirectCast(myObjectVariable, Integer) Console.WriteLine("anotherIntegerVariable: {0}", _ anotherintegervariable) wynik działania programu: myobjectvariable: 123 anotherintegervariable: 123

Aby być pewnym, co tak naprawdę jest rozpakowywane, używa się TypeOf(): ' unboxing (must be explicit) If TypeOf (myobjectvariable) Is Integer Then Dim anotherintegervariable As Integer = _ DirectCast(myObjectVariable, Integer) Console.WriteLine("anotherIntegerVariable: {0}", _ anotherintegervariable) End If Klasy zagnieżdżone Klasy zagnieżdżone są to klasy, których deklaracje są umieszczone w innych klasach. O klasach zagnieżdżonych mówi się, że są to klasy wewnętrzne, zaś klasy, w których zamieszczone są definicje klas wewnętrznych nazywane są klasami zewnętrznymi. Klasy zagnieżdżone mają dostęp do wszyskich członków klasy zewnętrznej, tj. w metodach klasy zagnieżdżonej można odwoływać się do wszystkich, w tym prywatnych, członków klasy zewnętrznej. Klasa zagnieżdżona może być zadeklarowane w klasie zewnętrznej jako private, wtedy nie jest widoczna dla innych klas. Jeśli zaś klasa zagnieżdżona ma modyfikator dostępu public, jest ona widoczna wewnątrz bloku klasy zewnętrznej. Np. jeśli Button jest klasą zewnętrzną, zaś Location jest klasą zagnieżdżoną w Button o dostępie public, to do klasy zagnieżdżonej odwołać się można przez: Button.Location. Przykład użycia klasy Fraction z klasą zagnieżdżoną FractionArtist (zadaniem klasy zagnieżdżonej jest wypisywanie informacji na ekranie). Option Strict On Imports System Public Class Fraction Private numerator As Integer Private denominator As Integer Public Sub New( _ ByVal numerator As Integer, ByVal denominator As Integer) Me.numerator = numerator Me.denominator = denominator 'New Public Overrides Function ToString() As String Return [String].Format("{0}/{1}", numerator, denominator) End Function 'ToString ' Nested Class Class FractionArtist Public Sub Draw(ByVal f As Fraction) Console.WriteLine("Drawing the numerator: {0}", f.numerator) Console.WriteLine( _ "Drawing the denominator: {0}", f.denominator) 'Draw 'FractionArtist 'Fraction

Public Class Tester Shared Sub Main() Dim f1 As New Fraction(3, 4) Console.WriteLine("f1: {0}", f1.tostring()) Dim fa As New Fraction.FractionArtist fa.draw(f1) 'Main 'Tester Metoda Draw klasy zagnieżdżonej wprowadza dane na ekran. Co ciekawe, metoda ta ma dostęp do prywatnych zmiennych numerator i denominator obiektu klasy zewnętrznej. Gdyby nie była to metoda klasy zagnieżdżonej dostęp do zmiennych prywatnych byłby niemożliwy. Uwaga: aby utworzyć instancję klasy zagnieżdżonej należy wywołać jej konstruktor z nazwą w pełni kwalifikowaną: Dim fa As New Fraction.FractionArtist( ) Struktury Struktury w dużej mierze wyglądają jak klasy. Różnią się od nich jednak tym, że są lżejsze - nie zajmują tak dużo miejsca w pamięci niż klasy Struktury mogą zawierać konstruktory,, właściwości, metody, pola, operatory, typy zagnieżdżone, indeksowników. Jednak mimo tych podobieństw struktury nie posiadają możliwości dziedziczenia, nie posiadają destruktorów, co więcej, są elementami typu wartość (a nie referencja). Jeśli używa się struktur jako elementów tablic, jest to efektywniejsza implementacja niż używanie w tym samym celu klas. Sytuacja odwraca się w przypadku użycia struktur w kolekcjach. Kolekcje są klasami oczekującymi referencji, więc jeśli użyje się struktur, to każdy element wstawiany lub wyciągany z kolekcji będzie musiał zostać poddany operacji opakowania i rozpakowanie. Jest to mniej efektywne niż użycie klas w tym samym celu. Składnia definicji struktury jest następująca: [ <attrlist> ] [{ Public Protected Friend Protected Friend Private }] [ Shadows ] Structure name [ Implements interfacenames ] variabledeclarations [ proceduredeclarations ] End Structure Elementy tej składni zostały omówione przy okazji opisu deklaracji klas. Uwaga: w deklaracji struktur nie można inicjalizować jej elementów. Znaczy to, że poniższy kod nie jest poprawny: Public Structure Punkt Dim x As Integer = 1 Dim y As Integer = 1 End Structure A oto większy przykład deklaracji i użycia struktury: Option Strict On Imports System Namespace StructureDemonstration ' declare a Structure named Location

Public Structure Location ' the structure has private data Private myxval As Integer Private myyval As Integer ' constructor Public Sub New( _ ByVal xcoordinate As Integer, ByVal ycoordinate As Integer) myxval = xcoordinate myyval = ycoordinate 'New ' property Public Property XVal() As Integer Get Return myxval End Get Set(ByVal Value As Integer) myxval = Value End Set End Property Public Property YVal() As Integer Get Return myyval End Get Set(ByVal Value As Integer) myyval = Value End Set End Property ' Display the structure as a String Public Overrides Function ToString() As String Return String.Format("{0}, {1}", xval, yval) End Function 'ToString End Structure 'Location Class Tester Public Sub Run() ' create an instance of the structure Dim loc1 As New Location(200, 300) ' display the values in the structure Console.WriteLine("Loc1 location: {0}", loc1) ' invoke the default constructor Dim loc2 As New Location Console.WriteLine("Loc2 location: {0}", loc2) ' pass the structure to a method myfunc(loc1) ' redisplay the values in the structure Console.WriteLine("Loc1 location: {0}", loc1) 'Run

' method takes a structure as a parameter Public Sub myfunc(byval loc As Location) ' modify the values through the properties loc.xval = 50 loc.yval = 100 Console.WriteLine("Loc1 location: {0}", loc) 'myfunc Shared Sub Main() Dim t As New Tester t.run() 'Main 'Tester End Namespace 'StructureDemonstration Wynik działania programu: Loc1 location: 200, 300 Loc2 location: 0, 0 Loc1 location: 50, 100 Loc1 location: 200, 300