Ćwiczenie VB3.4 Struktura Try...Catch, obiekt Err, metoda Err.Raise
(Strukturalna obsługa wyjątków)
Jeśli wpiszemy do okna tekstowego zamiast cyfr – litery (np. abc), a następnie spróbujemy ten ciąg znaków przekonwertować na liczbę - instrukcją, np. CSng(txtLiczba.Text) lub
Single.Parse(txtLiczba.Text) działanie programu zostanie przerwane i wyświetlony zostanie komunikat
„Konwersja z ciągu ”abc” na typ Single nie jest prawidłowa” przy instrukcji A = CSng(TextBox1.Text)
lub
„Nieprawidłowy format ciągu wejściowego” przy instrukcji A = Single.Parse(TextBox1.Text)
patrz rys. 1 i 2.
Rysunek 1
Rysunek 2
Taką sytuację nazywamy błędem czasu wykonania (run time error), a środowisko pracy Visual Basic określa ją jako nie obsłuŜony wyjątek.
Po takim komunikacie program zatrzymuje się (zawiesza) - nie moŜe być dalej wykonywany i naleŜy go przerwać.
Aby program nie przerywał działania w sposób nieoczekiwany przez uŜytkownika lecz pozwolił dalej np. wprowadzać dane i posługiwać się nim - stosujemy własna strukturę obsługi błędów (tzw. wyjątków).
Struktura ta ma postać:
Try
'Kod chroniony Catch ex As Exception
'Instrukcje wykonywane przy błędzie.
'Własna obsługa błędów.
Finally
'Blok opcjonalny.
'Instrukcje wykonywane niezaleŜnie od tego 'czy błąd się pojawił czy nie pojawił.
End Try
Obiekt ex jest to obiekt wyjątek, którego właściwości moŜemy wykorzystywać.
Często strukturę tę skracamy – opuszczając blok Finally:
Try
'Kod chroniony Catch ex As Exception
'Instrukcje wykonywane przy błędzie 'Własna obsługa błędów.
End Try
lub jeszcze bardziej upraszczamy gdy nie zamierzamy korzystać z obiektu ex:
Try
'Kod chroniony Catch
'Instrukcje wykonywane przy błędzie 'Własna obsługa błędów.
End Try
Najprostsze wykorzystanie struktury Try... Catch
i jej efekt na rys. 3:Po odczytaniu takiego komunikatu moŜemy poprawić wpisana wartość, kliknąć przycisk i program będzie kontynuował działanie.
Obiekt ex (Exception - wyjątek)
Aby posłuŜyć się obiektem ex (Exception - wyjątek) moŜemy wykorzystać jego właściwości Message lub wprost wyświetlić metodą ToString jego zawartość.
Kod Try
'Kod chroniony
n = CInt(TextBox1.Text)
TextBox3.Text = "N = " & n.ToString Catch ex As Exception
'Komunikat jak na rys. 4
MsgBox("ex.Message: " & ex.Message, MsgBoxStyle.Information, _ "Własna obsługa błędów 1")
'Komunikat jak na rys. 5
MsgBox("ex.ToString: " & ex.ToString, MsgBoxStyle.Exclamation, _ "Własna obsługa błędów 1")
End Try
przy braku danych w oknie tekstowym wyświetla komunikaty:
Rysunek 4. Własna obsługa błędów 1, ex.Message
Rysunek 5. Własna obsługa błędów 2, ex.ToString
Który rodzaj komunikatu wybrać autor programu moŜe zadecydować sam (podpowiadam, Ŝe wersja 1, rys. 4, jest bardziej zrozumiała).
Obiekt Err
Gdy pojawia się błąd czasu wykonania „wie o nim wszystko” obiekt Err. MoŜemy z tej wiedzy skorzystać. UŜytecznymi dla nas właściwościami tego obiektu są:
Err.Number – zwraca numer błędu
Err.Description – zwraca krótki opis błędu.
Np. kod:
Try
'Kod chroniony
A = CSng(TextBox1.Text)
TextBox3.Text = "A = " & A.ToString Catch
'Instrukcje wykonywane przy błędzie
MsgBox("Err.Number = " & Err.Number & vbCrLf & _ "Err.Description = " & Err.Description) End Try
wyświetli komunikat jak na rys. 6 (porównaj z rys. 4)
Rysunek 6 A jeśli komunikat zredagujemy jak w kodzie poniŜej:
MsgBox("Proszę poprawić dane." & vbCrLf & _
Err.Description, MsgBoxStyle.Information, "Błąd nr " & Err.Number) UŜytkownik zobaczy go jak na rys. 7
Komunikat zaleŜny od numeru błędu
W bloku instrukcji Catch wykonywanych przy błędzie, moŜemy instrukcją warunkową badać numer błędu (Err.Number) i w zaleŜności od numeru błędu wyświetlać odpowiedni komunikat.
Np. kod poniŜej oblicza silnię. Silnia liczby 13 przekracza wartość maksymalnej liczby mieszczącą się w zmiennej typu Integer.
Sytuacja taka moŜe generować komunikaty zwracające uwagę uŜytkownikowi na rodzaj błędu - mówiący o niezgodności typów, rys. 8 lub przepełnieniu, rys. 9.
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click Dim s As Integer = 1
Dim i, n As Integer Try
'Kod chroniony
n = CInt(TextBox1.Text) For i = 1 To n
s = s * i Next
TextBox3.Text = s.ToString Catch
'Instrukcje wykonywane przy błędzie If Err.Number = 13 Then
'Komunikat przy błędzie nr 13 (rys. 8) MsgBox("Niezgodność typów." & vbCrLf & _
"Proszę poprawić dane.", MsgBoxStyle.Information, _ "Błąd nr " & Err.Number)
ElseIf Err.Number = 6 Then
'Komunikat przy błędzie nr 6, rys. 9 MsgBox("Przepełnienie." & vbCrLf & _
"Liczba jest zbyt duŜa.", MsgBoxStyle.Question, _ "Błąd nr " & Err.Number)
Else
'Komunikat przy błędach o pozostałych numerach MsgBox("Proszę poprawić dane." & vbCrLf & _
Err.Description, MsgBoxStyle.Critical, _ "Błąd nr " & Err.Number)
End If End Try End Sub
Rysunek 8 Rysunek 9
Samodzielne (programowe) generowanie błędów i ich obsługa
ZadanieObliczyć objętość walca V mając dane: promień podstawy R i wysokość H. Formularz niech ma postać jak na rys. 10.
Gdy tworzymy tę aplikację – moŜemy przewidzieć sytuację, gdy uŜytkownik wprowadzi błędne dane, np. wartości spoza zakresu (w zadaniu są to wartości mniejsze lub równe zero). MoŜemy teŜ obsłuŜyć ją programowo bez struktury Try ... Catch:
R = CSng(txtR.Text) If R <= 0 Then
MsgBox("Błąd danych: R <= 0", MsgBoxStyle.Critical, "Złe dane") Exit Sub
End If
W takiej sytuacji naleŜałoby teŜ warunkowo przerwać dalsze wykonywanie programu instrukcją Exit Sub. Efekt takiego rozwiązania pokazuje rys. 11.
Podobnie naleŜałoby zabezpieczyć błędnie wprowadzone dane dla wysokości walca H.
Wygodniejszym jednak sposobem jest posłuŜenie się strukturą Try ... Catch, a pojawienie się sytuacji, którą uwaŜamy za błędną, zasygnalizować metodą Err.Raise().
Rysunek 10
Rysunek 11
Kod
Private Sub btnOblicz_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnOblicz.Click Dim R, H, V As Single
Try
R = CSng(txtR.Text)
'If R <= 0 Then
' MsgBox("Błąd danych: R <= 0", MsgBoxStyle.Critical, _ "Złe dane")
' Exit Sub 'End If
If R <= 0 Then
Err.Raise(9000, "Test promienia. ", "Promień R <= 0") 'Err.Raise(Number, Source, Description)
End If
H = CSng(txtH.Text) If H <= 0 Then
Err.Raise(9001, "Test wysokości.", "Wysokość H <= 0") 'Err.Raise(Number, Source, Description)
End If
V = Math.PI * R ^ 2 * H txtV.Text = V.ToString Catch ex As Exception
MsgBox(Err.Source & Err.Description, _
MsgBoxStyle.Exclamation, "Błąd nr " & _ Err.Number)
'MsgBox("ex.Message: " & ex.Message, MsgBoxStyle.Information, _ "błąd nr " & Err.Number)
End Try End Sub
Posługiwanie się metodą Raise pozwala w strukturze Catch umieścić tylko jedną instrukcję (np.
pojedynczy Message Box) drukującą komunikaty o błędach i róŜnicować ją podczas własnego generowania błędu.
W kodzie pokazano teŜ „zakomentowane” fragmenty kodu do innej obsługi błędów. Proszę
„odkomentować” i zobaczyć róŜnicę w działaniu.
Programowe generowanie błędów i ich obsługa przy pracy z podprogramami
Gdy tworzymy aplikację składającą się z podprogramów i/lub funkcji - moŜliwość samodzielnego (programowego) określania numeru błędu, jego źródła i opisu jest najwygodniejszym sposobemposłuŜenia się strukturą Try ... Catch. Tworzymy ją na jak najwyŜszym poziomie (tu w programie głównym), a pojawienie się błędu w procedurach lub funkcjach obsłuŜymy metodą Err.Raise().
Zadanie to samo
Obliczyć objętość walca V mając dane promień podstawy R i wysokość H. Formularz niech ma zatem postać jak na rys. 10.
Pole podstawy walca i jego objętość obliczymy posługując się oddzielnymi funkcjami, a błędy, które mogą powstać „gdzieś głęboko” w funkcjach – obsłuŜymy w programie głównym strukturą Try ... Catch z pojedynczą instrukcją drukującą (róŜnorodne) komunikaty o błędach.
Kod
Private Sub btnOblicz_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) Handles btnOblicz.Click Dim R, H, V As Single
Try
R = CSng(txtR.Text) H = CSng(txtH.Text)
V = Objetosc(PolePodstawy(R), H) txtV.Text = V.ToString
Catch
MsgBox(Err.Source & " - błąd danych: " & Err.Description, _ MsgBoxStyle.Exclamation, "Błąd nr " & _
Err.Number) End Try
End Sub
Private Function PolePodstawy(ByRef R As Single) As Single If R <= 0 Then
Err.Raise(9000, "Funkcja PolePodstawy", "Promień R <= 0") 'Err.Raise(Number, Source, Description) End If
PolePodstawy = Math.PI * R ^ 2 End Function
Private Function Objetosc(ByVal pole As Single, _
ByVal wysokosc As Single) As Single If wysokosc <= 0 Then