Чи зашифрується код у програмі нарешті, якщо я поверну значення у блоці "Спроба"


237

Я переглядаю товариш для коду і кажу, що він використовував оператор return всередині блоку спробу. Чи все-таки код у розділі "Нарешті" все ще спрацьовує, хоча решта блоку спробу не працює?

Приклад:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}


Поводитися тут означає: зловити. Навіть порожнього блоку вилову у вашому глобальному обробнику достатньо. Крім того , є винятки , які не можуть бути оброблені: StackOverflowException, ExecutionEngineExceptionдеякі з них. А оскільки з ними неможливо обробити, finallyзапуск не буде працювати.
Авель

8
@Abel: Ви, здається, говорите про іншу ситуацію. Це питання стосується повернення в tryблок. Нічого про різкі переривання програми.
Джон Скіт

6
@Abel: Я не впевнений, що ви маєте на увазі під "поверненням після винятку", але це, здається, не про що тут питають. Подивіться на код - перший вислів tryблоку є returnтвердженням. (Друга заява цього блоку недосяжна і призведе до попередження.)
Джон Скіт

4
@Abel: Дійсно, і якби питання було "Чи буде кодекс, нарешті, завжди виконується у будь-якій ситуації", це було б актуальним. Але це не те, про що питали.
Джон Скіт

Відповіді:


265

Проста відповідь: так.


9
@Edi: Гм, я не бачу, які фонові нитки стосуються цього. В основному, якщо весь процес не припиняється, finallyблок буде виконуватися.
Джон Скіт

3
Довга відповідь - вам не обов’язково отримати остаточний блок для запуску, якщо трапилося щось катастрофічне, наприклад, переповнення стека, виняток із пам'яті, якась аварія чи якийсь випадок, або якщо хтось відключить вашу машину в потрібний час . Але для всіх намірів і цілей, якщо ви не робите щось дуже неправильне, нарешті блок завжди буде спрацьовувати.
Ендрю Роллінгс

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

200

Зазвичай, так. Зрештою, розділ гарантовано виконує все, що трапиться, включаючи винятки або оператор return. Виняток із цього правила - це асинхронний виняток, що відбувається в потоці ( OutOfMemoryException, StackOverflowException).

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


154

Ось невеликий тест:

class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("before");
        Console.WriteLine(test());
        Console.WriteLine("after");
    }

    static string test()
    {
        try
        {
            return "return";
        }
        finally
        {
            Console.WriteLine("finally");
        }
    }
}

Результат:

before
finally
return
after

10
@CodeBlend: Це стосується того, коли WriteLineнасправді виконується результат виклику методу. У цьому випадку returnвиклик встановлює результат методів і, нарешті, записує на консоль - до виходу методу. Тоді WriteLineметод Main в основному викидає текст із зворотного дзвінка.
NotMe

38

Цитуючи з MSDN

нарешті , використовується , щоб гарантувати операторний блок коду виконується незалежно від того, як попередній спроба буде блок вийшов .


3
Ну, крім виняткових випадків Мехрдада, нарешті також не буде викликано, якщо ви налагоджуєте у блоці спробу, а потім припиніть налагодження :). Нічого в житті не гарантується, здається.
StuartLC

1
Не зовсім, цитуючи з MSDN : Однак, якщо виняток є необробленим, виконання остаточного блоку залежить від того, як спрацьовує операція розмотування винятку. Це, в свою чергу, залежить від налаштування вашого комп’ютера. .
Авель

1
@Abel робить вирішальний момент тут. Всі можуть бачити, як остаточний блок не виконується, якщо, наприклад, програма переривається через диспетчер завдань. Але ця відповідь, як це виглядає, явно неправильна: finallyнасправді нічого не гарантує навіть для абсолютно звичайних програм (що трапилося викинути цей виняток).
Пітер - Відновити Моніку

19

Як правило, так, нарешті, побіжить.

Для наступних трьох сценаріїв, нарешті, ЗАВЖДИ запуститься:

  1. Винятків не відбувається
  2. Синхронні винятки (винятки, що трапляються в нормальному потоці програми).
    Сюди входять винятки, сумісні з CLS, які випливають із винятків, сумісних із системою System.Exception, та винятків, що не відповідають CLS, які не походять від System.Exception. Винятки, що не відповідають стандартам CLS, автоматично завершуються RuntimeWrappedException. C # не може викидати скарги, що не стосуються CLS, але такі мови, як C ++, можуть. C # може вводити код, написаний мовою, яка може кидати винятки, що не відповідають стандартам CLS.
  3. Асинхронна ThreadAbortException
    Станом на .NET 2.0, ThreadAbortException більше не заважатиме остаточно запуститися. ThreadAbortException тепер піднімається до або після остаточно. Нарешті завжди буде працювати і не буде перервано потоком переривання потоку, доки спроба була фактично введена до того, як відбудеться переривання потоку.

Наступний сценарій, нарешті, не запуститься:

Асинхронний StackOverflowException.
Станом на .NET 2.0 переповнення стека призведе до припинення процесу. Остаточно не буде запущено, якщо не буде застосовано додаткового обмеження, щоб зробити нарешті СЕР (область обмеженого виконання). СЕР не слід використовувати в загальному коді користувача. Їх слід використовувати лише там, коли критично важливо, щоб код очищення завжди виконувався - адже процес все-таки вимикається з переповненням стека і тому всі керовані об'єкти будуть очищені за замовчуванням. Таким чином, єдине місце, на яке СЕР повинен бути актуальним, - це ресурси, які виділяються поза процесом, наприклад, некеровані ручки.

Зазвичай некерований код обгортається деяким керованим класом перед тим, як його використовуватиме код користувача. Клас керованої обгортки зазвичай використовує SafeHandle для загортання керованої ручки. SafeHandle реалізує критичний фіналізатор та метод випуску, який запускається в CER, щоб гарантувати виконання коду очищення. З цієї причини ви не повинні бачити CERs, засмічений кодом користувача.

Отже, той факт, що нарешті не працює на StackOverflowException, не повинен впливати на код користувача, оскільки процес все одно закінчиться. Якщо у вас є певний крайній випадок, коли вам потрібно очистити якийсь некерований ресурс, поза SafeHandle або CriticalFinalizerObject, тоді використовуйте CER наступним чином; але зауважте, це погана практика - некерована концепція повинна бути абстрагована для керованого класу та відповідних SafeHandle (-ів) за задумом.

наприклад,

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{ 
    // This is *NOT* a CER
}
finally
{
    // This is a CER; guaranteed to run, if the try was entered, 
    // even if a StackOverflowException occurs.
}

Зауважте, що навіть CER не працюватиме у випадку ДП. На той момент, коли ви це писали, документація MSDN про CER була неправильною / неповною. ДП запустить FailFastвнутрішньо. Єдиний спосіб, коли мені вдалося це зробити, - налаштувати хостинг виконання CLR . Зауважте, що ваша точка все ще справедлива для деяких інших асинхронних винятків.
Авель

10

Є дуже важливий виняток із цього, про який я не бачив згадки в жодних інших відповідях, і який (після програмування на C # протягом 18 років) я не можу повірити, що не знав.

Якщо кинути або викликати виключення будь-якого роду всередині ваш catchблок (не просто дивно StackOverflowExceptionsі речі того ж роду), і ви не маєте весь try/catch/finallyблок всередині іншогоtry/catch блоку, ваш finallyблок не буде виконуватися. Це легко продемонструвати - і якби я сам цього не бачив, враховуючи те, як часто я читав, що лише справді дивні, крихітні куточки випадків можуть спричинити finallyневиконання блоку, я б не повірив у це.

static void Main(string[] args)
{
    Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
    try
    {
        Console.WriteLine("Inside try but before exception.");
        throw new Exception("Exception #1");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
        throw;
    }
    finally
    {
        Console.WriteLine("This never gets executed, and that seems very, very wrong.");
    }

    Console.WriteLine("This never gets executed, but I wasn't expecting it to."); 
    Console.ReadLine();
}

Я впевнений, що в цьому є причина, але це химерно, що це не широко відомо. (Це зазначено тут , наприклад, але не де - небудь в цьому конкретному питанні.)


Ну, finallyблок просто не є спійманим для catchблоку.
Пітер -

7

Я усвідомлюю, що я спізнююся з партією, але в сценарії (який відрізняється від прикладу ОП), коли дійсно виняток становлять штати MSDN ( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ): "Якщо виняток не вловлюється, виконання остаточного блоку залежить від того, чи операційна система вирішить запустити операцію розмотування виключення."

Остаточно блок гарантовано виконується лише в тому випадку, якщо якась інша функція (наприклад, Main) надалі підсилює стек викликів. Ця деталь, як правило, не є проблемою, оскільки всі програми часу (CLR та OS) програми C # запускаються на більшості безкоштовних ресурсів, якими володіє процес, коли він закінчується (файли обробляють тощо). Однак у деяких випадках це може бути вирішальним: половина операції з базою даних, яку ви хочете здійснити відповідно. розмотати; або деяке віддалене з'єднання, яке може не закриватися автоматично ОС, а потім блокує сервер.


3

Так. Це насправді є головним моментом остаточного твердження. Якщо не відбувається щось катестрофне (поза пам'яттю, відключення комп'ютера тощо), остаточне твердження завжди повинно бути виконане.


1
Прошу не погодитися. Ср. моя відповідь. Цілком нормального винятку в блоці спробу достатньо для обходу finallyблоку, якщо цей виняток ніколи не потрапляє. Це мене здивувало ;-).
Пітер - Відновіть Моніку

@ PeterA.Schneider - Це цікаво. Звичайно, наслідки цього насправді не сильно змінюються. Якщо нічого зі стека викликів не збирається вилучити виняток, то це повинно означати (якщо я правильно розумію), що процес збирається припинити. Так це схоже на ситуацію, що підтягується. Я здогадуюсь про те, що ви завжди повинні мати випадок вищого рівня або необроблений виняток.
Джефрі Л Вітлідж

Саме це я і розумію. Я також виявив, що це дивно. Правда: найпоширеніші ресурси будуть випущені автоматично, зокрема пам'ять та відкриті файли. Але ресурси, про які ОС не знає - відкритий підключення до сервера або бази даних - можуть залишатися відкритими на віддаленій стороні, оскільки вони ніколи не були належним чином відключені; розетки продовжують затримуватися і т. д. Я думаю, доцільно мати обробника винятків верхнього рівня, так.
Пітер - Відновіть Моніку


2

нарешті не буде запущено у випадку, якщо ви виходите із програми за допомогою System.exit (0); а саме

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

результат був би просто: спробуйте


4
Ви відповідаєте невірною мовою, я вважаю, питання було про c#, але це, здається, так Java. І крім того, у більшості випадків System.exit()є натяком на поганий дизайн :-)
z00l

0

99% сценаріїв буде гарантовано, що код всередині finallyблоку запуститься, однак, подумайте про цей сценарій: у вас є потік, у якого є try-> finallyблок (ні catch), і ви отримаєте необроблений виняток у цьому потоці. У цьому випадку нитка вийде і їїfinally блок не буде виконуватися (програма може продовжувати працювати в цьому випадку)

Цей сценарій є досить рідкісним, але це лише показати, що відповідь не ЗАВЖДИ «Так», це більшість часу «Так», а іноді, у рідкісних умовах, «Ні».


0

Основна мета блоку нарешті - виконати все, що написано всередині нього. Це не повинно залежати від того, що трапиться при спробі вловити. Однак із системою System.Environment.Exit (1) програма вийде, не переходячи до наступного рядка коду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.