如果 finally 块抛出异常,究竟会发生什么?
具体来说,如果在 finally 块中途抛出异常会发生什么。是否会调用此块中的其余语句(之后)?
我知道异常会向上传播。
如果 finally 块抛出异常究竟会发生什么?
该异常会向外传播,并且将(可以)在更高级别进行处理。
您的 finally 块将不会在引发异常的点之后完成。
如果 finally 块在处理较早的异常期间正在执行,则第一个异常将丢失。
C# 4 语言规范第 8.9.5 节:如果 finally 块抛出另一个异常,则终止当前异常的处理。
对于这样的问题,我通常会在 Visual Studio 中打开一个空的控制台应用程序项目并编写一个小示例程序:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
catch (Exception ex)
{
Console.WriteLine("Inner catch block handling {0}.", ex.Message);
throw;
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
当您运行程序时,您将看到 catch
和 finally
块执行的确切顺序。请注意,在抛出异常后的 finally 块中的代码将不会被执行(事实上,在这个示例程序中,Visual Studio 甚至会警告您它检测到无法访问的代码):
Inner catch block handling exception thrown from try block. Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
附加说明
正如 Michael Damatov 所指出的,如果您不在(内部)catch
块中处理它,try
块中的异常将被“吃掉”。实际上,在上面的示例中,重新抛出的异常并没有出现在外部 catch 块中。为了更清楚地了解以下稍微修改的示例:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
从输出中可以看出,内部异常是“丢失”(即被忽略):
Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
finally
块将(几乎)总是被执行,这在这种情况下也适用于内部 finally 块(只需自己尝试示例程序(在 a 的情况下将不会执行 finally 块)不可恢复的异常,例如 EngineExecutionException
,但在这种情况下,您的程序无论如何都会立即终止)。
如果存在未决的异常(当 try
块有 finally
但没有 catch
时),则新的异常会替换该异常。
如果没有挂起的异常,它就像在 finally
块之外抛出异常一样工作。
catch
块(重新)抛出异常,则异常也可能处于未决状态。
保存“原始异常”(在 try
块中抛出)并牺牲“最终异常”(在 finally
块中抛出)的快速(且相当明显)代码段,以防原始异常对您更重要:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
执行上述代码时,“原始异常”会向上传播调用堆栈,“最终异常”会丢失。
传播异常。
在另一个异常处于活动状态时引发异常将导致第一个异常被第二个(后来的)异常替换。
下面是一些说明发生了什么的代码:
public static void Main(string[] args)
{
try
{
try
{
throw new Exception("first exception");
}
finally
{
//try
{
throw new Exception("second exception");
}
//catch (Exception)
{
//throw;
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
运行代码,你会看到“第二个异常”
取消注释 try 和 catch 语句,您将看到“第一个异常”
同时取消注释 throw;声明,您将再次看到“第二个异常”。
几个月前我也遇到过这样的事情,
private void RaiseException(String errorMessage)
{
throw new Exception(errorMessage);
}
private void DoTaskForFinally()
{
RaiseException("Error for finally");
}
private void DoTaskForCatch()
{
RaiseException("Error for catch");
}
private void DoTaskForTry()
{
RaiseException("Error for try");
}
try
{
/*lacks the exception*/
DoTaskForTry();
}
catch (Exception exception)
{
/*lacks the exception*/
DoTaskForCatch();
}
finally
{
/*the result exception*/
DoTaskForFinally();
}
为了解决这样的问题,我做了一个实用程序类
class ProcessHandler : Exception
{
private enum ProcessType
{
Try,
Catch,
Finally,
}
private Boolean _hasException;
private Boolean _hasTryException;
private Boolean _hasCatchException;
private Boolean _hasFinnallyException;
public Boolean HasException { get { return _hasException; } }
public Boolean HasTryException { get { return _hasTryException; } }
public Boolean HasCatchException { get { return _hasCatchException; } }
public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
public Dictionary<String, Exception> Exceptions { get; private set; }
public readonly Action TryAction;
public readonly Action CatchAction;
public readonly Action FinallyAction;
public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
{
TryAction = tryAction;
CatchAction = catchAction;
FinallyAction = finallyAction;
_hasException = false;
_hasTryException = false;
_hasCatchException = false;
_hasFinnallyException = false;
Exceptions = new Dictionary<string, Exception>();
}
private void Invoke(Action action, ref Boolean isError, ProcessType processType)
{
try
{
action.Invoke();
}
catch (Exception exception)
{
_hasException = true;
isError = true;
Exceptions.Add(processType.ToString(), exception);
}
}
private void InvokeTryAction()
{
if (TryAction == null)
{
return;
}
Invoke(TryAction, ref _hasTryException, ProcessType.Try);
}
private void InvokeCatchAction()
{
if (CatchAction == null)
{
return;
}
Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
}
private void InvokeFinallyAction()
{
if (FinallyAction == null)
{
return;
}
Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
}
public void InvokeActions()
{
InvokeTryAction();
if (HasTryException)
{
InvokeCatchAction();
}
InvokeFinallyAction();
if (HasException)
{
throw this;
}
}
}
并像这样使用
try
{
ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
handler.InvokeActions();
}
catch (Exception exception)
{
var processError = exception as ProcessHandler;
/*this exception contains all exceptions*/
throw new Exception("Error to Process Actions", exception);
}
但是如果你想使用参数和返回类型那就是另一回事了
异常向上传播,应在更高级别进行处理。如果异常未在更高级别处理,则应用程序崩溃。 “finally”块执行在抛出异常的地方停止。
不管是否有异常,“finally”块都保证执行。
如果在 try 块中发生异常之后正在执行“finally”块,并且如果未处理该异常并且 finally 块抛出异常
那么try块中发生的原始异常就丢失了。
public class Exception
{
public static void Main()
{
try
{
SomeMethod();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void SomeMethod()
{
try
{
// This exception will be lost
throw new Exception("Exception in try block");
}
finally
{
throw new Exception("Exception in finally block");
}
}
}
我必须这样做是为了捕捉一个错误,试图关闭一个由于异常而从未打开的流。
errorMessage = string.Empty;
try
{
byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);
webRequest = WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "text/xml;charset=utf-8";
webRequest.ContentLength = requestBytes.Length;
//send the request
using (var sw = webRequest.GetRequestStream())
{
sw.Write(requestBytes, 0, requestBytes.Length);
}
//get the response
webResponse = webRequest.GetResponse();
using (var sr = new StreamReader(webResponse.GetResponseStream()))
{
returnVal = sr.ReadToEnd();
sr.Close();
}
}
catch (Exception ex)
{
errorMessage = ex.ToString();
}
finally
{
try
{
if (webRequest.GetRequestStream() != null)
webRequest.GetRequestStream().Close();
if (webResponse.GetResponseStream() != null)
webResponse.GetResponseStream().Close();
}
catch (Exception exw)
{
errorMessage = exw.ToString();
}
}
如果创建了 webRequest 但在执行过程中发生了连接错误
using (var sw = webRequest.GetRequestStream())
然后 finally 会捕获一个异常,试图关闭它认为是打开的连接,因为 webRequest 已经创建。
如果 finally 里面没有 try-catch,这段代码会在清理 webRequest 时导致未处理的异常
if (webRequest.GetRequestStream() != null)
从那里代码将退出而没有正确处理发生的错误,从而导致调用方法出现问题。
希望这有助于作为一个例子
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
CodeA 和 CodeB 抛出的异常的处理方式是一样的。
finally
块中抛出的异常没有什么特别之处,将其视为代码 B 抛出的异常。
它引发异常;)您可以在其他一些 catch 子句中捕获该异常。
不定期副业成功案例分享
ThreadAbortException
,否则整个 finally 块将首先完成,因为它是一个关键部分。