确定执行finally时是否从try块抛出异常

时间:2017-09-11 18:48:01

标签: powershell exception

我有一些像这样构建的代码

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

当然,人们通常应该避免抛出最终的代码。但它可能发生。当它发生时,一个不幸的副作用是原始异常丢失。所以我在finally中要做的第一件事是关于try中抛出的异常的日志信息,如果抛出的话。

但是,我怎样才能确定在try块中是否确实发生了异常,一旦我在最后?有光滑的方式吗?我想抓住捕获中的异常。我可以在try的末尾设置一个布尔值,这表示没有抛出异常,但我并不是每次都必须这样做的忠实粉丝。这看起来像这样:

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

我可以做得更好吗?

2 个答案:

答案 0 :(得分: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()
    }
}

答案 1 :(得分:0)

关于Powershell 5.1的

This page没有明确涵盖在“finally”块中抛出异常的情况,但是说Powershell行为类似于C#行为。接受this SO question about C# behavior的答案表明:

  1. 不执行抛出异常的“finally”块中的代码,
  2. 如果在处理先前的异常期间执行了“finally”块,那么第一个异常就会丢失
  3. 所以我认为你真正想要的是这样的:

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

    只有在“try”块中抛出异常时才会执行catch块,但是在每种情况下都会发生“finally”块(并且可以判断是否在原始“try”块中抛出了异常)检查标志的值。)

    当然,如果可以在“finally”块中抛出异常并且您想要处理它,那么您将需要将整个业务包装在另一个“try”中。