Ruby/Przetwarzanie wyjątków: rescue: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Szymon wro (dyskusja | edycje)
Szymon wro (dyskusja | edycje)
Nie podano opisu zmian
Linia 4:
 
<pre>
rubyirb(main):001:0> fileplik = open("'jakis_plik"')
ERRErrno: (eval):1:in `open'ENOENT: No such file or directory - jakis_plik
from (irb):1:in `initialize'
from (irb):1:in `open'
from (irb):1
</pre>
 
Solidny program powinien radzić sobie z takimi sytuacjami sensownie i wdzięcznie. Sprostanie temu wymaganiu może być irytującym zadaniem. Od programistów języka C oczekuje się sprawdzania wyniku każdego wywołania systemowego które potencjalnie mogło się nie powieść oraz natychmiastowego zdecydowania, co należy zrobić:
 
<source lang="cpp">
<pre>
FILE *plik = fopen("jakis_plik", "r");
if (plik == NULL) {
Linia 21 ⟶ 24:
}
...
</presource>
 
Jest to bardzo męcząca praktyka, którą programiści mają w zwyczaju traktować niedbale i pomijać, czego rezultatem jest to, że program źle sobie radzi z wyjątkami. Z drugiej strony, porządne wykonanie tej pracy czyni programy trudnymi do czytania, ponieważ duża ilość kodu obsługi wyjątków przesłania właściwą logikę programu.
Linia 27 ⟶ 30:
W Rubim, tak jak w wielu współczesnych językach programowania, możemy radzić sobie z wyjątkami poszczególnych bloków kodu oddzielnie, co jednak skutecznie acz nie nadmiernie obciąża programistę lub każdego, kto będzie potem czytał kod. Blok kodu oznaczony oznaczony słowem <tt>begin</tt> wykonuje się dopóki nie napotka na wyjątek, które powoduje przekierowanie kontroli do bloku zarządzania błędami, który jest oznaczony przez <tt>rescue</tt>. Jeżeli nie wystąpi żaden wyjątek, kod z bloku <tt>rescue</tt> nie jest używany. Następująca metoda zwraca pierwszą linię z pliku tekstowego lub <tt>nil</tt> jeżeli napotka wyjątek:
 
<source lang="ruby">
<pre>
def pierwsza_linia(nazwa_pliku)
begin
Linia 38 ⟶ 41:
end
end
</presource>
 
Będą występować sytuacje, gdy będziemy chcieli kreatywnie pracować nad problemem. Tutaj, jeśli plik, który żądamy jest niedostępny, możemy spróbować użyć standardowego wejścia:
 
<source lang="ruby">
<pre>
begin
plik = open("jakis_plik")
Linia 54 ⟶ 57:
# ... tutaj obsluguj wyjatki.
end
</presource>
 
Słowo kluczowe <tt>retry</tt> może być używane w bloku <tt>rescue</tt>, by wystartować blok <tt>begin</tt> od początku. Pozwala to nam przepisać poprzedni przykład nieco zwięźlej:
 
<source lang="ruby">
<pre>
nazwap = "jakis_plik"
begin
Linia 67 ⟶ 70:
retry
end
</presource>
 
Jednakże, mamy tutaj pewną wadę. Nieistniejący plik sprawi, że pętla ta będzie powtarzana w nieskończoność. Musisz zwracać uwagę na tego rodzaju pułapki podczas przetwarzania wyjątków.
Linia 73 ⟶ 76:
Każda biblioteka Rubiego podnosi wyjątek jeśli wystąpi jakiś błąd. Ty również możesz podnosić wyjątki jawnie w kodzie. By podnieść wyjątek użyj słowa kluczowego <tt>raise</tt>. Przyjmuje ono jeden argument, którym powinien być łańcuch znakowy opisujący wyjątek. Argument jest wprawdzie opcjonalny, jednak nie powinien być pomijany. Będzie on mógł być później dostępny za pomocą specjalnej zmiennej globalnej <tt>$!</tt>.
 
<source lang="ruby">
<pre>
ruby> begin
ruby> raise "blad testowy"
| raise "test2"
blad testowy
| rescue
ruby> begin
puts "Wystapil blad: test2#{$!}"
| raise "test2"
end
| rescue
#=> | puts "Wystapil blad: #{$!}"test2
</source>
| end
Wystapil blad: test2
nil
</pre>
 
Zmienna <tt>$!</tt> zwraca konkretny obiekt który jest podklasą klasy <tt>Exception</tt>. Klasa <tt>Exception</tt> jest nadklasą (niekoniecznie wprost) wszystkich wyjątków. Cechę tę można efektywnie wykorzystać podczas definiowania różnych bloków obsługujących poszczególne typy wyjątków.
 
<source lang="ruby">
<pre>
ruby> begin
| plik = open("jakis_plik")
| rescue SystemCallError
| puts "Blad WE/WY: #{$!}"
| rescue Exception
| puts "Blad: #{$!}"
| end
#=> Blad WE/WY: No such file or directory - jakis_plik
</source>
nil
</pre>
 
Operacja otwarcia pliku generuje wyjątek będący podklasą <tt>SystemCallError</tt> więc zostanie wykonany blok obsługujący ten wyjątek. Interpreter po kolei sprawdza wszystkie bloki <tt>rescue</tt> i wykonuje pierwszy pasujący. Z tego też powodu nie należy umieszczać <tt>rescue Exception</tt> jako pierwszego, gdyż <tt>Exception</tt> jako nadklasa wszystkich wyjątków będzie tu zawsze pasować i blok obsługujący <tt>Exception<tt> będzie zawsze wykonywany.
Linia 103 ⟶ 102:
Jeżeli podmienimy <tt>plik = open("jakis_plik")</tt> na np. <tt>raise "jakis blad"</tt> wykonany zostanie blok <tt>rescue</tt> obsługujący <tt>Exception</tt>:
 
<source lang="ruby">
<pre>
ruby> begin
| raise "jakis blad"
| rescue SystemCallError
| puts "Blad WE/WY: #{$!}"
| rescue Exception
| puts "Blad: #{$!}"
| end
#=> Blad: jakis blad
</source>
nil
</pre>
 
Zamiast zmiennej <tt>$!</tt> można używać zmiennych nazwanych stosując operator <tt>=></tt> i składnię przypominającą definiowanie wpisu haszatablicy asocjacyjnej.
<source lang="ruby">
<pre>
begin
# ... jakis kod ...
Linia 124 ⟶ 122:
puts "Blad: #{e}"
end
</presource>
<noinclude>
{{ProstaNawigacja|spis=Ruby|poprzart=Ruby/Stałe klasowe|poprz=Stałe klasowe|nastart=Ruby/Przetwarzanie wyjątków: ensure|nast=Przetwarzanie wyjątków: ensure}}