Спробує / нарешті (без лову) бульбашка виняток?


115

Я майже впевнений, що відповідь ТАК. Якщо я використовую блок "Спробуйте остаточно", але не використовую блок "Catch", то будь-які винятки будуть "Bubble". Правильно?

Якісь думки щодо практики взагалі?

Сет

Відповіді:


130

Так, це абсолютно буде. Припустимо, що ваш finallyблок не кидає винятку, звичайно, у такому випадку це ефективно "замінить" той, який був викинутий.


13
@David: Ви не можете повернутися з остаточного блоку в C #.
Джон Скіт

3
Документація msdn також підтверджує цю відповідь: Крім того, ви можете
широкосмуговий

60

Якісь думки щодо практики взагалі?

Так. Будьте обережні . Коли ваш остаточний блок запущений, цілком можливо, що він працює, оскільки було кинуто необроблений, несподіваний виняток . Це означає, що щось порушено , і щось абсолютно несподіване може статися .

У цій ситуації, мабуть, випадок, що вам взагалі не слід запускати код. Код остаточно блоку може бути побудований так, щоб припустити, що підсистеми, від яких він залежить, є здоровими, бо насправді вони можуть бути глибоко зламані. Код в остаточному блоці може погіршити ситуацію.

Наприклад, я часто бачу такі речі:

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

Автор цього кодексу думає: "Я роблю тимчасові мутації до стану світу; мені потрібно відновити стан до того, яким він був до того, як мене покликали". Але давайте подумаємо про всі способи, як це могло піти не так.

По-перше, доступ до ресурсу вже може бути відключений абонентом; у такому випадку цей код знову вмикає його, можливо, передчасно.

По-друге, якщо DoSomethingToTheResource кидає виняток, це правильно зробити, щоб дозволити доступ до ресурсу ??? Код, який управляє ресурсом, несподівано порушується . Цей код говорить, по суті, "якщо код управління порушений, переконайтеся, що інший код може викликати цей зламаний код якомога швидше, так що він також може жахливо вийти з ладу ". Це здається поганою ідеєю.

По-третє, якщо DoSomethingToTheResource кидає виняток, то як ми можемо знати, що EnableAccessToTheResource також не викине виключення? Яка б жахливість не мала, використання ресурсу також може вплинути на код очищення; в цьому випадку вихідний виняток буде втрачено, і проблему буде важче діагностувати.

Я схильний писати такий код без використання пробних блоків:

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

Зараз держава не мутується, якщо цього не потрібно. Тепер стан абонента не псується. І тепер, якщо DoSomethingToTheResource виходить з ладу, ми не повторно вмикаємо доступ. Ми припускаємо, що щось глибоко порушено, і не ризикуємо погіршити ситуацію, намагаючись зберегти запущений код. Нехай абонент вирішує проблему, якщо зможе.

Тож коли корисно запустити остаточний блок? По-перше, коли очікується виняток. Наприклад, ви можете очікувати, що спроба блокування файлу може бути невдалою, оскільки його заблокував хтось інший. У цьому випадку є сенс зловити виняток і повідомити про це користувачеві. У цьому випадку зменшується невизначеність того, що порушено; Ви навряд чи погіршите ситуацію, прибираючи.

По-друге, коли ресурс, який ви прибираєте, - це дефіцитний системний ресурс. Наприклад, є сенс закрити ручку файлу остаточно блоком. ("Використання" - це, звичайно, лише інший спосіб написання блоку спробу. Нарешті.) Вміст файлу може бути пошкодженим, але зараз ви нічого не можете з цим зробити. Обробку файлів врешті-решт буде закрито, тому це може бути швидше, ніж пізніше.


7
Ще більше прикладів того, як ми насправді не дійшли разом, як галузь, коли справа стосується помилок. Не те, що я маю щось краще запропонувати, ніж винятки, але я сподіваюся, що майбутнє може спричинити щось більш імовірно, що правильний курс дій буде сприйнятий досить легко.
Джон Скіт

У vb.net можна за кодом у блоці "Нарешті" дізнатися, який виняток очікує на розгляд. Якщо встановити ієрархію винятків, щоб відрізнити "не зробив це; стан інакше гаразд" від "стан порушено", можна було б виконати блок "Остаточний" лише в тому випадку, якщо винятковий виняток не є одним із поганих. Мені подобається, щоб розпорядники розпорядників / очищення збирали винятки і кидали DisposerFailedException (який є в категорії "погано" і включає оригінальний виняток як InnerException). Я хотів би побачити стандартний інтерфейс iDisposableEx з Dispose (Ex як виняток), щоб полегшити це.
supercat

Хоча я розумію, що є випадки, коли може статися виняток, коли об’єкт знаходиться в поганому стані, а спроба очищення погіршить ситуацію, я думаю, що такі проблеми слід вирішувати в коді очищення, можливо, за допомогою делегатів. Наприклад, обгортка блокування може виявити делегат функції "CheckIfSafeToUnlock (наприклад, як виняток)", який можна встановити в моменти, коли заблокований об'єкт знаходиться у недійсному стані та очищений в іншому випадку. Перш ніж звільнити замок, обгортка могла перевірити делегата; якщо він не порожній, він може запустити його та звільнити замок лише у випадку, якщо він повернеться як true.
supercat

Маючи обгортку, використовуючи такий делегат, дозволить коду, який маніпулював станом об'єкта, вибрати відповідну дію очищення, якщо вона була порушена. Такі дії можуть включати в себе відновлення структури даних та повернення True, викидання іншого "більш суворого" винятку, який обертає оригінальний, або надання оригінального винятку перколату під час повернення False.
supercat

2
Еріку, дякую за чудову відповідь. Напевно, я мав би задати два питання, тому що ви і Джон правильні. У будь-якому випадку, ви прихильні. Дякуємо за увагу до питання.
Сет Спірмен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.