Zajęcia 4.
Własne funkcje i procedury
Dany jest walec zewnętrzny o promieniu podanym w TextBox ‘txtRz’ i wysokości w TextBox ‘txtHz’. W walcu tym są trzy wydrążenia w kształcie walców o promieniach i wysokościach podanych odpowiednio w obiektach TextBox: ‘txtRw1’, ‘txtHw1’, ‘txtRw2’, ‘txtHw2’, ‘txtRw3’, ‘txtHw3’. Aplikacja służy do obliczania objętości tak powstałej bryły. Procedura obsługi zdarzenia ‘Click’ przycisku
‘btnObliczObjetoscBryly’ wywołuje dla danych każdego z czterech walców naszą funkcję o nazwie
‘walecObjetosc’, która przyjmuje dwa argumenty typu tekstowego (String) przekazane przez wartość (ByVal).
Pierwszy argument jest interpretowany jako promień walca, drugi jako wysokość. Funkcja zwraca objętość walca jako liczbę rzeczywistą typu (Double). Zwracane w kolejnych wywołaniach funkcji wartości są podstawianie pod zmienne typu (Double), które są następnie wykorzystane do obliczenia objętości bryły.
Aby użyć funkcji należy w obliczanym wyrażeniu podać jej nazwę i w nawiasach okrągłych listę parametrów aktualnych (oddzielonych przecinkami), na których ma przeprowadzić obliczenia. Natomiast by funkcja zwróciła wartość należy użyć wewnątrz jej kodu słowa kluczowego (Return), a po nim należy umieścić wyrażenie, którego wartość ma przyjąć funkcja. Wykonanie tej instrukcji kończy jednocześnie działanie funkcji i następujące po niej instrukcje, o ile takie istnieją, nie zostaną wykonane. Zwrócenie wartości jest też możliwe w stylu Visual Basic 6.0, czyli przez podstawienie wartości pod nazwę funkcji. Jednak takie postępowanie nie kończy działania funkcji, aż do napotkania słowa kluczowego (End Function) kończącego kod funkcji albo (Exit Function), które wymusza wcześniejsze jej zakończenie.
Do funkcji czy procedury można przesłać zmienne przez wartość (ByVal) albo przez referencje (ByRef).
Zadeklarowanie zmiennej powoduje przydzielenie jej w pamięci ciągłego kawałka miejsca o rozmiarze zależnym od typu zmiennej. Na początek tego miejsca wskazuje adres. Dzięki temu adresowi i typowi zmiennej program wie ile
bitów skąd ma odczytać i jak je zinterpretować. Przekazanie argumentu przez wartość wymusza utworzenie
w osobnej przestrzeni adresowej kopii zmiennej i do tego nowo zarezerwowanego miejsca skopiowanie ciągu bitów z miejsca, gdzie jest przechowywana oryginalna zmienna. Ta nowo utworzona kopia jest dostępna jedynie wewnątrz funkcji czy procedury i wszystkie zmiany jej wartości nie wpływają na wartość zmiennej oryginalnej utworzonej na wyższym poziomie. Natomiast przekazanie argumentu przez referencję jest jednoznaczne z udostępnieniem podprogramowi adresu do zmiennej oryginalnej. Ponieważ w tym przypadku funkcja czy procedura działa
na oryginale, więc wszystkie zmiany wartości zmiennej w podprogramie są widoczne na zewnątrz. Podsumowując, (ByVal) należy użyć, by jedynie przesłać parametr do podprogramu, co jednocześnie zabezpiecza przed przypadkową zmianą wartości zmiennej oryginalnej. Natomiast (ByRef) należy użyć, aby wyprowadzić na zewnątrz wyniki
działania podprogramu.
Konstrukcję (Try - Catch) należy użyć w najbardziej zewnętrznym podprogramie. Wszystkie błędy powstałe w bloku (Try) bez względu na to, czy powstały w instrukcji bezpośrednio tu umieszczonej, czy w instrukcji wewnątrz procedur i funkcji w tym bloku wywoływanych (i na każdym następnym poziomie wywołań) zostaną przechwycone przez (Catch) i mogą tu być obsłużone. Znajomość tego faktu znacznie upraszcza tworzenie skomplikowanych aplikacji, gdyż odpada problem poinformowania procedury nadrzędnej o błędzie zaistniałym w procedurach podrzędnych, z których ta korzysta. W środowisku .NET jest już zdefiniowana cała gama błędów. Istnieje obiekt
‘Err’, który zawiera informacje dotyczące błędów powstałych w czasie wykonywania programu. Właściwość
‘Number’ zawiera numer błędu. Numery od 0 do 512 są zarezerwowane na błędy zdefiniowane w systemie.
Natomiast numery od 513 do 65535 są dostępne dla błędów definiowanych przez programistę. Właściwość
‘Description’ zawiera opis błędu. Właściwość (‘Source’) zawiera ciąg znaków określający nazwę źródła, gdzie błąd został wygenerowany (najczęściej nazwa obiektu lub aplikacji). Obiekt ten posiada również bardzo użyteczną metodę ‘Raise’ umożliwiającą programiście wygenerowanie błędu, który można obsłużyć jak samo, jak błędy zdefiniowane w systemie. Pierwszym i jedynym obowiązkowym argumentem tej metody jest liczba całkowita od 0 do 65535, która określa numer błędu (ustawienie właściwości ‘Err.Number’). Wszystkie pozostałe argumenty są opcjonalne. Dlatego aby wygenerować błąd zdefiniowany w systemie wystarczy podąć jego numer. Drugim argumentem jest łańcuch znaków określający źródło błędu (ustawienie właściwości ‘Err.Source’). Trzecim argumentem jest łańcuch znaków będący opisem błędu (ustawienie właściwości ‘Err.Description’). Istnieją jeszcze dwa parametry definiujące plik pomocy i kontekst w tym pliku dotyczący generowanego błędu, lecz w ramach laboratorium nie będą wykorzystywane.
Przycisk ‘btnObliczWszystko’ oprócz objętości końcowej bryły liczy również objętości i pola powierzchni dla poszczególnych walców składowych. Procedura obsługi zdarzenia ‘Click’ tego przycisku wywołuje dla danych każdego z czterech walców naszą procedurę o nazwie ‘walecObliczenia’, która przyjmuje w sumie sześć
argumentów. Dwa argumenty typu tekstowego (String) przekazywane są przez wartość (ByVal). Pierwszy argument jest interpretowany jako promień walca, drugi jako wysokość. Cztery pozostałe argumenty służą do wyprowadzenia na zewnątrz procedury wyników dokonanych przez nią obliczeń. Są to liczby typu (Double) przekazane przez
referencję (ByRef), dzięki temu operujemy na oryginalnych zmiennych zadeklarowanych w procedurze wywołującej.
Wszystkie zmiany ich wartości wewnątrz procedury są widoczne w procedurze wywołującej, co pozwala
na wyprowadzenie obliczeń z procedury na zewnątrz. Parametry te są interpretowane odpowiednio jako: objętość;
pole powierzchni podstawy, pole powierzchni bocznej i pole powierzchni całkowitej walca. Proszę zwrócić uwagę, że w procedurze wywołującej trzy zmienne dotyczące pól powierzchni są wspólne dla wszystkich walców. Wynika to z faktu, iż po dokonaniu obliczeń dla jednego walca, wyniki są zapisywane w obiektach TextBox i mogą już zostać nadpisane obliczeniami dla następnego walca.
Procedura o nazwie ‘wyczyscNaZmiane’ obsługuje zdarzenia ‘TextChanged’ dla ośmiu obiektów TextBox do wprowadzania danych (białe na formularzu). Nazwa procedury nie ma znaczenia pod warunkiem, że jest unikalna w ramach formularza. Istotna jest natomiast, posłowie kluczowym (Handles), lista oddzielona przecinkami
‘obiekt.zdarzenie’ określająca, które zdarzenia, jakich obiektów będą obsłużone przez tą procedurą.
Zarówno w procedurze obsługi zdarzenia ‘Click’ przycisku ‘btnWyczysc’, jak i w procedurze obsługi zdarzenia ‘TextChanged’ dla obiektów TextBox służących do wprowadzania danych, należy wyczyścić wszystkie obiekty TextBox prezentujące wyniki (zielone na formularzu). Dlatego napisano własna procedurę bez parametrów o nazwie ‘czyscWyniki’, która została wywołana w tych dwóch ww. procedurach. Procedurę ‘czyscWyniki’
można byłoby wywołać również w procedurze obsługi zdarzenia ‘Click’ przycisku ‘btnObliczWszystko’ w sytuacji, gdy zostaje wygenerowany błąd, gdy objętość bryły nie jest dodatnia.
Kod aplikacji:
Public Class frmWalecSub
Private Sub btnKoniec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnKoniec.Click
Application.Exit() 'Zakończenie aplikacji
End Sub
' Własna procedura czyszcząca obiekty TextBox prezentujące wyniki (zielone na formularzu)
Private Sub czyscWyniki() txtVw1.Clear()
txtVw2.Clear() txtVw3.Clear() txtVz.Clear() txtPbW1.Clear() txtPbW2.Clear() txtPbW3.Clear() txtPbZ.Clear() txtPcW1.Clear() txtPcW2.Clear() txtPcW3.Clear() txtPcZ.Clear() txtPpW1.Clear() txtPpW2.Clear() txtPpW3.Clear() txtPpZ.Clear()
txtObjetoscBryly.Clear() End Sub
' do wprowadzania danych (białe na formularzu)
' Procedura obsługująca zdarzenia TextChanged dla obiektów TextBox
' Nazwa procedury nie ma znaczenia, aby była unikalna
' Istotna jest posłowie kluczowym Handles (oddzielona przecinkami) lista obiekt.zdarzenie
' określająca, które zdarzenia, jakich obiektów będą obsłużone tą procedurą
Private Sub wyczyscNaZmiane(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles txtHw1.TextChanged, txtHw2.TextChanged, txtHw3.TextChanged, txtHz.TextChanged, _ txtRw1.TextChanged, txtRw2.TextChanged, txtRw3.TextChanged, txtRz.TextChanged
Call czyscWyniki() 'Wywołanie procedury czyszczącej wszystkie pola TextBox prezentujące wyniki
End Sub
' Procedura obsługująca zdarzenie klik na przycisku Wyczyść
' Czyści wszystkie obiekty TextBox na formularzu
Private Sub btnWyczysc_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnWyczysc.Click
txtHw1.Clear() txtHw2.Clear() txtHw3.Clear() txtHz.Clear() txtRw1.Clear() txtRw2.Clear() txtRw3.Clear() txtRz.Clear()
Call czyscWyniki() 'Wywołanie procedury czyszczącej wszystkie pola TextBox prezentujące wyniki
End Sub
' Własna funkcja zwracająca objętość walca jako liczbę typu Double
' Przyjmuje dwa argumenty typu tekstowego (String) przekazane przez wartość (ByVal)
' Pierwszy argument jest interpretowany jako promień walca, drugi jako wysokość ' czyli wymuszające utworzenie wewnątrz funkcji kopii przekazywanej zmiennej
Private Function walecObjetosc(ByVal txtR As String, ByVal txtH As String) As Double 'Deklaracja zmiennych liczbowych dla promienia i wysokości walca
Dim dblR, dblH As Double
dblR = CDbl(txtR) 'Zamiana typu ze String na Double dla promienia
If dblR <= 0 Then 'prawda, gdy promień nie jest dodatni
'wygenerowanie własnego błędu, gdy promień nie jest dodatni
'Err.Raise(numer_błędu, źródło_błędu, opis_błędu)
'chr(34) zwraca znak " 'Err.Raise(Err.Number, Err.Source, Err.Description)
Err.Raise(9999, "Sprawdzenie danych walca", _
"Promień " & Chr(34) & txtR & Chr(34) & " nie jest większy od zera") End If
dblH = CDbl(txtH) 'Zamiana typu ze String na Double dla wysokości
If dblH <= 0 Then 'prawda, gdy wysokość nie jest dodatnia
'wygenerowanie własnego błędu, gdy wysokość nie jest dodatnia
Err.Raise(9998, "Sprawdzenie danych walca", _
"Wysokość " & Chr(34) & txtH & Chr(34) & " nie jest większa od zera") End If
'Zwrócenie wartości przez funkcję
'słowo kluczowe Return zwraca wartość wyrażenia podanego po nim i kończy działanie funkcji
Return Math.PI * dblR ^ 2 * dblH
'pod nazwę funkcji, lecz nie kończy to funkcji aż do końca jej kodu 'innym sposobem zwrócenia wartości przez funkcję jest przypisanie wartości wyrażenia
' alecObjetw osc = Math.PI * dblR ^ 2 * dblH End Function
Private Sub btnObliczObjetoscBryly_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnObliczObjetoscBryly.Click
'objętość walca zewnętrznego, wewnętrznych od 1 do 3 i objętość bryły
Dim dblVz, dblVw1, dblVw2, dblVw3, dblVbryly As Double Try 'próbuj
dblVz = walecObjetosc(txtRz.Text, txtHz.Text) 'wywołanie funkcji dla walca zewnętrznego
dblVw1 = walecObjetosc(txtRw1.Text, txtHw1.Text) 'wywołanie funkcji dla walca wewnętrznego nr 1
dblVw2 = walecObjetosc(txtRw2.Text, txtHw2.Text) 'wywołanie funkcji dla walca wewnętrznego nr 2
dblVw3 = walecObjetosc(txtRw3.Text, txtHw3.Text) 'wywołanie funkcji dla walca wewnętrznego nr 2
dblVbryly = dblVz - dblVw1 - dblVw2 - dblVw3 'obliczenie całkowitej objętości bryły
If dblVbryly <= 0 Then 'prawda, gdy objętość bryły nie jest dodatnia
'wygenerowanie własnego błędu, gdy objętość bryły nie jest dodatnia
Err.Raise(9997, "Obliczanie objętości bryły", _ "Złe dane:" & vbCrLf & _
"Suma objętości walców wewnętrznych jest większa niż objętość walca zewnętrznego") End If
txtObjetoscBryly.Text = dblVbryly.ToString 'wyświetlenie objętości bryły
Catch ex As Exception 'gdy błąd przechwyć
'czy wygenerowanych przez nas za pomocą metody Raise obiektu Err 'Obsługa zaistniałych błędów, również tych powstałych wewnątrz wywoływanych funkcji
MsgBox("Błąd numer: " & Err.Number.ToString & vbCrLf & Err.Description, _ MsgBoxStyle.Exclamation, Err.Source)
End Try End Sub
' Własna procedura przyjmuje w sumie sześć argumentów
' czyli wymuszające utworzenie wewnątrz funkcji kopii przekazywanej zmiennej ' Dwa argumenty typu tekstowego (String) przekazane przez wartość (ByVal)
' Pierwszy argument jest promień walca, drugi jako wysokość
' Cztery pozostałe argumenty są liczbami typu Double przekazane przez referencję (ByRef),
' dzięki temu operujemy na oryginalnych zmiennych zadeklarowanych w procedurze wywołującej.
' Wszystkie zmiany ich wartości są widoczne w procedurze wywołującej,
' co pozwala na wyprowadzenie obliczeń z procedury na zewnątrz
' są interpretowane odpowiednio jako:
' objętość; pole powierzchni podstawy, bocznej i całkowitej walca
Private Sub walecObliczenia(ByVal txtR As String, ByVal txtH As String, _
ByRef dblV As Double, ByRef dblPp As Double, ByRef dblPb As Double, ByRef dblPc As Double) 'Deklaracja zmiennych liczbowych dla promienia i wysokości walca
Dim dblR, dblH As Double
dblR = CDbl(txtR) 'Zamiana typu ze String na Double dla promienia
If dblR <= 0 Then 'prawda, gdy promień nie jest dodatni
'wygenerowanie własnego błędu, gdy promień nie jest dodatni
Err.Raise(9999, "Sprawdzenie danych walca", _
"Promień " & Chr(34) & txtR & Chr(34) & " nie jest większy od zera") End If
dblH = CDbl(txtH) 'Zamiana typu ze String na Double dla wysokości
If dblH <= 0 Then 'prawda, gdy wysokość nie jest dodatnia
'wygenerowanie własnego błędu, gdy wysokość nie jest dodatnia
Err.Raise(9998, "Sprawdzenie danych walca", _
"Wysokość " & Chr(34) & txtH & Chr(34) & " nie jest większa od zera") End If
'Obliczenia wykonywane poniżej podstawiane są pod zmienne przekazane do procedury przez referencję,
'dzięki temu operujemy na oryginalnych zmiennych zadeklarowanych w procedurze wywołującej
'i wyniki są tam widoczne
dblPp = Math.PI * dblR ^ 2 'obliczenie pola powierzchni podstawy
dblPb = 2 * Math.PI * dblR * dblH 'obliczenie pola powierzchni bocznej
dblPc = 2 * dblPp + dblPb 'obliczenie pola powierzchni całkowitej
dblV = dblPp * dblH 'obliczenie objętości walca
End Sub
Private Sub btnObliczWszystko_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnObliczWszystko.Click
'objętość walca zewnętrznego, wewnętrznych od 1 do 3 i objętość bryły
Dim dblVz, dblVw1, dblVw2, dblVw3, dblVbryly As Double 'pole powierzchni podstawy, bocznej i całkowitej walca
Dim dblPp, dblPb, dblPc As Double Try 'próbuj
'wywołanie procedury dla walca zewnętrznego
Call walecObliczenia(txtRz.Text, txtHz.Text, dblVz, dblPp, dblPb, dblPc) txtVz.Text = dblVz.ToString 'wyświetlenie objętości dla walca zewnętrznego
txtPpZ.Text = dblPp.ToString 'wyświetlenie pola powierzchni podstawy dla walca zewnętrznego
txtPbZ.Text = dblPb.ToString 'wyświetlenie pola powierzchni bocznej dla walca zewnętrznego
txtPcZ.Text = dblPc.ToString 'wyświetlenie pola powierzchni całkowitej dla walca zewnętrznego
'wywołanie procedury dla walca wewnętrznego nr 1
Call walecObliczenia(txtRw1.Text, txtHw1.Text, dblVw1, dblPp, dblPb, dblPc) txtVw1.Text = dblVw1.ToString 'wyświetlenie objętości dla walca wewnętrznego nr 1
txtPpW1.Text = dblPp.ToString 'wyświetlenie pola pow. podstawy dla walca wewnętrznego nr 1
txtPbW1.Text = dblPb.ToString 'wyświetlenie pola pow. bocznej dla walca wewnętrznego nr 1
txtPcW1.Text = dblPc.ToString 'wyświetlenie pola pow. całkowitej dla walca wewnętrznego nr 1
'wywołanie procedury dla walca wewnętrznego nr 2
Call walecObliczenia(txtRw2.Text, txtHw2.Text, dblVw2, dblPp, dblPb, dblPc) txtVw2.Text = dblVw2.ToString 'wyświetlenie objętości dla walca wewnętrznego nr 2
txtPpW2.Text = dblPp.ToString 'wyświetlenie pola pow. podstawy dla walca wewnętrznego nr 2
txtPbW2.Text = dblPb.ToString 'wyświetlenie pola pow. bocznej dla walca wewnętrznego nr 2
txtPcW2.Text = dblPc.ToString 'wyświetlenie pola pow. całkowitej dla walca wewnętrznego nr 2
'wywołanie procedury dla walca wewnętrznego nr 3
Call walecObliczenia(txtRw3.Text, txtHw3.Text, dblVw3, dblPp, dblPb, dblPc) txtVw3.Text = dblVw3.ToString 'wyświetlenie objętości dla walca wewnętrznego nr 3
txtPpW3.Text = dblPp.ToString 'wyświetlenie pola pow. podstawy dla walca wewnętrznego nr 3
txtPbW3.Text = dblPb.ToString 'wyświetlenie pola pow. bocznej dla walca wewnętrznego nr 3
txtPcW3.Text = dblPc.ToString 'wyświetlenie pola pow. całkowitej dla walca wewnętrznego nr 3
dblVbryly = dblVz - dblVw1 - dblVw2 - dblVw3 'obliczenie całkowitej objętości bryły
If dblVbryly <= 0 Then 'prawda, gdy objętość bryły nie jest dodatnia
'wygenerowanie własnego błędu, gdy objętość bryły nie jest dodatnia
Err.Raise(9997, "Obliczanie objętości bryły", _ "Złe dane:" & vbCrLf & _
"Suma objętości walców wewnętrznych jest większa niż objętość walca zewnętrznego") End If
txtObjetoscBryly.Text = dblVbryly.ToString 'wyświetlenie objętości bryły
Catch ex As Exception 'gdy błąd przechwyć
'Obsługa zaistniałych błędów, również tych powstałych wewnątrz wywoływanych procedur
'czy wygenerowanych przez nas za pomocą metody Raise obiektu Err
MsgBox("Błąd numer: " & Err.Number.ToString & vbCrLf & Err.Description, _ MsgBoxStyle.Exclamation, Err.Source)
End Try
End ClassEnd Sub