Arhn - архитектура программирования

Определите, было ли выброшено исключение из блока try при выполнении команды finally.

ОБНОВЛЕНИЕ, май 2021 г. - Когда я изначально задал этот вопрос, основная вещь, которая сделала этот вопрос актуальным (для меня), заключалась в том, что при повторном генерировании исключения из улова через простой throw (сам по себе) исходный стек исключений был потерян. Так что это сделано с помощью улова, чтобы определить, было ли исключено исключение.

Это некорректное поведение потери стека было исправлено где-то между заданием вопроса (2017 г.) и сейчас. Таким образом, простой захват и повторное создание (вызов throw без других аргументов) теперь является наиболее простым способом обнаружения исключения, которое было сгенерировано из блока finally. Спасибо @JohnLBevan за его ответ, который дал мне понять, что повторный выброс из улова больше не является проблемой.

ОРИГИНАЛЬНЫЙ ВОПРОС:

У меня такой код структурирован

try{
...
}
finally{
...
<code that may throw>
}

Конечно, обычно следует избегать кода, который вызывает файл finally. Но такое бывает. И когда это происходит, одним неприятным побочным эффектом является потеря исходного исключения. Итак, первое, что я хотел бы сделать в finally, - это записать информацию об исключении, сгенерированном при попытке, если оно было сгенерировано.

Но как я могу определить, возникло ли исключение в блоке try, когда я нахожусь в окончательном? Есть хитрый способ? Я не хочу перехватывать исключение в уловке. Я могу установить логическое значение в конце попытки, которое указывало бы на то, что исключение не было сгенерировано, но я не большой поклонник необходимости делать это каждый раз. Это выглядело бы так:

$exceptionThrown = $true
try{
...
$exceptionThrown = $false
}
finally{
<if $exceptionThrown log info about it>
...
<code that may throw>
}

Могу я сделать лучше?

11.09.2017

  • Я предполагаю, что это версия этого вопроса для PowerShell: stackoverflow.com/questions/3301507/. Если Powershell не раскрывает то, чего не делает C #, я полагаю, что мне не повезло. 11.09.2017
  • Сразу понял, что написать код в finally, чтобы сделать что-то с исключением, сложнее, чем я надеялся. Я использовал $ Error [0], чтобы получить последнее исключение, которое работает нормально, если только код в блоке try не завершает работу раньше, не вызывая исключения - например, возврат или выход. В этом случае $ Error [0] либо не существует, либо является устаревшим значением ошибки для вашей текущей среды ... вздох ... 13.10.2017

Ответы:


1

Если единственная причина, по которой вы избегаете блока catch, заключается в том, что вы не хотите влиять на трассировку стека, вы можете использовать его, а затем повторно выбросить ошибку с исходным номером строки, используя throw без аргументов; таким образом, вы перебрасываете оригинал точно так же, как если бы вы не использовали блок catch. Например:

$exceptionInfo = $null
try {
    1/0 # cause some error
} catch {
    $exceptionInfo = $_.Exception # you could set a flag / whatever here; without knowing your requirement I can't advise further
    throw # you said you didn't want to catch it... but if you just don't want to impact the stack trace this is fine as we're rethrowing the original exception; not throwing a new one
} finally {
    if ($null -eq $exceptionInfo) {
        Write-Information 'Completed Successfully' -InformationAction Continue
    } else {
        Write-Warning "An error occurred $exceptionInfo"
    }
}

Если вы не хотите использовать блок catch и не хотите использовать определенную вами переменную для обозначения того, произошло ли исключение, вы можете использовать $Error; хотя вам может потребоваться сначала очистить его, поскольку он будет содержать все ошибки, возникшие в текущем сеансе ...

$Error.Clear()
try {
    1/0
} finally {
    if ($Error.Count) {
        Write-Warning "An error occurred $($Error[0])"
    } else {
        Write-Information 'Completed Successfully' -InformationAction Continue
    }
}

Как правило, вам не нужно определять, было ли что-то успешным в блоке finally; скорее:

  • Если у вас есть логика, которая должна выполняться только в случае успешного выполнения команды, поместите ее в блок TRY после строки, которая может вызвать исключение.
  • Если у вас есть логика, которая должна возникать только в том случае, если команда обнаруживает ошибку, поместите ее в блок catch (и используйте throw, если вы хотите повторно выбросить исходное исключение, чтобы оно продолжало всплывать впоследствии).
  • Если у вас есть логика, которая должна работать в любом случае, то здесь и появляется блок finally. Иногда вам может потребоваться знать состояние какого-либо компонента здесь (например, соединение с вашей базой данных все еще открыто), и это состояние могло измениться из-за исключение ... Если это так, обычно такие компоненты должны предоставлять свои собственные флаги.

Ниже представлена ​​приблизительная иллюстрация; реальный пример немного плох, так как я не мог придумать хороший и сжатый сценарий реального мира; но, надеюсь, вы уловили идею.

try {
    $con = Get-MyDbConnection
    New-DbRecord -Connection $con -Data $data 
} catch {
    Write-Log $_.Exception
} finally {
    if (($null -ne $con) -and ($con.IsConnected)) {
        $con.Disconnect()
    }
}
12.05.2021
  • Итак, еще в 2017 году, когда был задан вопрос, повторное выбрасывание через throw в catch приведет к потере исходной трассировки стека. Но комментарий к ответу на вопрос на странице stackoverflow.com/questions/13820140 указывает на то, что в какой-то момент это было исправлено. Я только что попробовал в Windows 10 1909, и сейчас он действительно работает нормально. Это отличные новости. 12.05.2021
  • Ах, хорошо - я не понимал, что исторически это не сработало; это многое объясняет. Спасибо / надеюсь, что информация будет полезна, даже если опоздал на 4 года;) 12.05.2021
  • Совершенно полезно. Вашей уверенности в ответе и высокой репутации было достаточно, чтобы заставить меня усомниться в своем 4-летнем «я», быстро просмотреть связанные вопросы и увидеть заметку о том, что поведение изменилось и исправлено. Обнаружены и другие дурацкие проблемы с исключениями PowerShell :-) Но быстрый эксперимент показал, что стек действительно теперь сохраняется. Может быть, когда-нибудь я разверну несколько виртуальных машин и попробую посмотреть, когда это будет исправлено. Спасибо! 12.05.2021

  • 2

    Эта страница о Powershell 5.1 явно не описывает случай выброса исключения внутри блока "finally", но говорит, что поведение Powershell аналогично поведению C #. И принятый ответ на этот вопрос SO о поведении C # означает, что:

    1. код в блоке "finally" за точкой, в которой было создано исключение, не выполняется, и
    2. если блок «finally» был выполнен во время обработки более раннего исключения, то это первое исключение теряется.

    Итак, я думаю, что вам действительно нужно что-то вроде этого:

      try { ... set flag ... }
      catch { ... adjust flag ... }
      finally { ... check flag ... }
    

    Блок catch выполняется только в том случае, если в блоке «try» возникло исключение, но блок «finally» происходит в каждом случае (и может определить, было ли выбрано исключение в исходном блоке «try», проверяя значение флага).

    Конечно, если в блоке «finally» может быть сгенерировано исключение, и вы хотите с этим справиться, тогда вам нужно будет обернуть весь этот бизнес еще одной «попыткой».

    11.09.2017
  • Важно, чтобы я не перехватил исключение, так как хочу, чтобы оно распространялось вверх по стеку. Похоже, что повторное создание в PowerShell, независимо от того, как вы это делаете, теряет исходную трассировку стека, так что это тоже нехорошо. А пока я смирился с тем, чтобы установить логическое значение в качестве последнего оператора моего блока try ... 12.09.2017
  • C # позволит вам повторно генерировать исключения и сохранить исходный контекст исключения (включая трассировку стека, местоположение источника и т. Д.), Но принятый ответ на следующий вопрос SO согласуется с вашей оценкой, что это, к сожалению, не работает в Powershell: stackoverflow.com/questions/13820140/ (хотя можно было бы вставить некоторые из наиболее полезных атрибутов исходного исключения в сообщение об ошибке повторно созданного исключения) 13.09.2017
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..