|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||
|
Ініціація
виключень
Вище вже
мовилося про те, що метод Processfilе просто передає виключення в процедуру Sub
Main, з якої він був викликаний. У процедурі Sub Mai n команда виклику теж
поміщена в блок Try-catch, тому виключення буде оброблено. З іншого боку,
таке рішення виглядає трохи наївно, а якщо написані вами класи використовуватимуться
іншими програмістами, воно стає просто небезпечним. Але навіть якщо справа абияк
обійдеться, користувачі вашої коди навряд чи будуть задоволені тим, що ви без розбору
передаєте виключення, не намагаючись їх обробити. Краще спробувати
по можливості «прибрати» за собою, а потім скористатися ключовим
словом Throw, щоб передати об'єкт виключення зухвалій стороні. В розділі 4
згадувалося про те, що в VB .NET не підтримується детерміноване завершення.
Отже, якщо ви створили об'єкт з методом D1 spose, цей метод слід
викликати перед тим, як ініціювати виключення. Сказане відноситься і до відкриття
файлів, і до отримання графічного контексту. У наступному фрагменті представлена
умовна структура подібної коди: Try '
Створення локального об'єкту з методом Dispose ' Код. який може ініціювати виключення Catch(e As Exception) local Object.dispose() Throw e; End Try Якщо ви не
викличете метод Dispose для свого локального об'єкту, то захоплені ресурси
так і не будуть звільнені. Адже посилання на об'єкт існує лише в локальному
коді; решта частин програми не володіє доступом до методу Dispose! З іншого
боку, причина, по якій виникло виключення, залишається в силі, тому про
виниклу проблему (наприклад, про невдалу операцію з файлом) потрібно повідомити
зухвалий код. Для цього слід наново ініціювати виключення командою
Throw, як це зроблено в другому виділеному рядку.
Втім, якщо ви дійсно хочете програмувати «як годиться»,
не обмежуйтеся простим перезапуском виключення. Постарайтеся зробити свій
код якомога більш інформативним і включите в об'єкт виключення додаткову
інформацію. Для цього є три можливості.
Рішення розташовані
за збільшенням пріоритету, і в ідеальному випадку слід завжди використовувати
пункт 3. На практиці програмісти при виборі керуються своєю оцінкою того,
яку інформацію про виключення необхідно передати для подальшої обробки. Для прикладу
представте таку ситуацію: з джерела даних читаються пари «ключ/значення»,
і для останнього ключа не знаходиться парного значення. Програма припускає,
що значення асоціюється з кожним ключем, тому при спробі читання виникає
неожіданно'е виключення введення-виводу (читання даних з файлу описане в розділі
9). Тепер ви хочете повідомити про той, що відбувається зухвалій стороні. Щоб додати у виключення рядок, можна скористатися спеціальною версією конструктора класу Exception: Public Sub New(Byval
message As String) У наступному
фрагменті в об'єкт Ioexception додається новий рядок з повідомленням про відсутність
значення для останнього ключа, після чого виключення ініціюється наново. Dim excep As
New Iqexception("Missing value for last key") Throw excep Отримавши ініційоване
виключення, зовнішній код отримує текст повідомлення методом Message класу Exception
і дізнається про виниклу проблему. Друга ситуація
реалізується елементарно завдяки головному правилу спадкоємства: похідний
клас завжди може використовуватися замість базового класу. Вам лише залишається
ініціювати виключення похідного класу, яке краще підходить для даної
ситуації. Останній випадок вимагає
деякої додаткової роботи, оскільки для цього потрібно буде визначити
клас, похідний від існуючого класу виключення. Припустимо, ви
хочете визначити новий клас виключення, похідний від System. 10. loexception. Новий
клас відрізняється від старого лише однією ReadOnly-свойством, що повертає
ключ, з яким не асоціюється парне значення: Public Class Lastvaluelostexception Inherits System.I0.I0.Exception Private mkey As String Public Sub New(Byval
thekey As String) Mybase.New("No value found for last key") mkey = thekey End
Sub Public Readonly
Property Lastkey() As String Get Return mkey End Get End Property End Class Звернете увагу:
ім'я створеного класу виключення завершується словом Exception. Це стандартне
правило, якому ми настійно рекомендуємо слідувати. Отримавши виключення
Lastvaluelostexception, програміст може скористатися властивістю Lastkey,
значення якої передається в конструкторі нового класу виключення, і
отримати ключ, що не асоціюється із значенням. Наступний рядок забезпечує
видачу правильній інформації методом Message базового класу Exception: Mybase.New("No
value found for last key") У цьому
рядку викликається конструктор базового класу (і кінець кінцем конструктор предка
Exception). Можливо,
ви відмітили, що в класі Lastvaluelostexception не перевизначаються інші
методи — такі, як метод Tostring, успадкований від Exception. У стандартних
ситуаціях об'єкти виключень завжди повинні виводити стандартні повідомлення. Як
використовувати створений клас в програмі? Наприклад, якщо останній ключ без парного
значення був рівний «oops», виключення ініціюватиметься наступною командою: Throw New Lastvaluelostexception("oops")
Ми
створили новий клас виключень, похідний від Ioexcepti on, тому що
потенційна проблема явно відносилася до категорії введення-виводу. Допустимо, ситуація
має більш загальний характер і для базового класу не існує інших очевидних
кандидатів, окрім класу Exception. Втім, це не зовсім вірно — кращий вибір
існує завжди. Ми настійно рекомендуємо вибирати як базового не сам клас
Exceptlon, а похідний від нього клас Appllcationexception. Річ у тому, що
.NET Framework розрізняє виключення, що виникли в результаті проблем виконавчого
середовища (наприклад, браки пам'яті або дискового простору) і проблем, обумовлених
роботою вашого застосування. Саме виключення другої категорії мають бути похідними
від Appllcationexcepti on, тому саме цей клас слід вибирати базовим
при визначенні узагальнених виключень в програмі. Виконавче
середовище допомагає зробити наступний крок. Ієрархія виключень розходиться на дві
гілки, показані на мал. 7.1.
Мал.
7.1. Дві основні
гілки ієрархії виключень Класи Exceptlon, Appllcationexcepti
on і Systemexcepti on володіють однаковою функціональністю. Існування
трьох класів замість одного — не більше ніж зручна абстракція, завдяки
якій стає простіше зрозуміти виключення, що виникають у ваших програмах.
Обробка
виключень у поєднанні з визначенням власних класів виключень дозволяє
повністю відмовитися від використання Goto. Наприклад, в главе
3 був приведений приклад виправданого застосування Goto для переривання вкладених
циклів, коли помилка відбувається у внутрішньому циклі. Програміст VB .NET у подібній
ситуації просто укладає весь цикл в блок Try-catch, як показано нижче: Sub Main() Dim
getdata As String Dim i, j
As Integer Dim e As System.I0.I0Exception Try For i = 1 To 10 For j = 1 To
100 Console.WriteC'Type the data, hit the Enter key between " & _ "ZZZ to end: ") getdata _ Console.ReadLine()
If getdata = "ZZZ" Then e New System.I0.I0Exception("Data
entry ended " & _ "at user
request") Throw e Else ' Обробка даних End If Next j Next i Catch Console.WriteLinete.Message) Console. Readline() End Try End Sub У приведеному вище
фрагменті виділені рядки не можна об'єднати конструкцією наступного вигляду: Dim e As New
System.IO.IOException("Data entry ended at user request") Унаслідок
правил видимості VB .NET об'єкт виключення опиниться недоступним в секції Catch.
При використанні блоків
Try-catch нерідко існує код, який повинен виконуватися як при нормальному
завершенні, так і при виникненні виключення. Наприклад, в обох випадках
слід закрити файли, викликати методи Dispose і так далі Навіть в простому прикладі,
приведеному на початку розділу, було потрібно команду Readline, щоб консольне
вікно залишалося на екрані до натиснення клавіші Enter. Щоб деякий
фрагмент виконувався незалежно від того, чи виникне в програмі виключення
чи ні, в блок Try-catch включається секція Finally, виділена в наступному
прикладі жирним шрифтом: Sub Main() Dim args().
argument As String args = Environment.
Getcommandlineargs() Try Processfile(argsd)) Catch Console.WriteLine("ERROR") Finally Console.WriteLine("Press
enter to end") Console.ReadLine() End Try End Sub
Рекомендації
по використанню виключень
Виключення
виглядають ефектно, і новачки часто схильні зловживати ними. Насправді, чи варто
витрачати час на аналіз призначеного для користувача введення, коли можна просто
ініціювати виключення? Не піддайтеся спокусі. При неправильному використанні
обробка виключень істотно уповільнює роботу програми. Нижче приведені
деякі рекомендації по використанню виключень в програмі.
|
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||