如果 finally 块抛出异常,会发生什么?
具体而言,如果异常在 finally 块的中途被抛出,那么在此块中剩余的语句(即在异常被抛出之后的语句)是否会被调用?
我知道异常会向上级传播。
如果 finally 块抛出异常,会发生什么?
具体而言,如果异常在 finally 块的中途被抛出,那么在此块中剩余的语句(即在异常被抛出之后的语句)是否会被调用?
我知道异常会向上级传播。
ThreadAbortException
,否则整个finally块将首先完成,因为它是一个关键部分。 - Dmytro ShevchenkoDispose
,并且数据无法写入,则使Dispose
静默失败将是危险的,特别是如果在Dispose
返回后没有异常挂起。不幸的是,Dispose
无法知道它是否被调用于成功或失败的using
块,因此无法避免在调用Dispose
时破坏任何可能挂起的异常,或者在出现问题时掩盖危险的静默故障。 - supercat对于这样的问题,我通常会在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 甚至会警告你它已检测到无法访问的代码):
内部 catch 块处理从 try 块抛出的异常。 内部 finally 块 外部 catch 块处理从 finally 块抛出的异常。 外部 finally 块
额外说明
正如 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");
}
}
}
从输出结果可以看出,内部异常被“丢失”(即被忽略):
内部 finally 块 由 finally 块抛出的异常被外部 catch 块捕获。 外部 finally 块
finally
块几乎总是会被执行的,包括这个例子中的内部 finally 块(你可以自己试试这个示例程序)。但如果出现无法恢复的异常(例如 EngineExecutionException
),则 finally 块将不会被执行,但在这种情况下,您的程序无论如何都会立即终止。 - Dirk Vollmar如果存在待处理的异常(当try
块有finally
但没有catch
时),新的异常将替换原有异常。
如果不存在待处理的异常,它就像在finally
块外抛出异常一样运行。
catch
块(重新)抛出异常,那么异常也可能会挂起。 - stakx - no longer contributing如果原始异常对您更重要,那么可以使用以下快速(且相当明显)的代码片段来保存“原始异常”(在try
块中抛出),并且放弃“finally异常”(在finally
块中抛出):
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
我不得不这样做来捕获由于异常而未打开流而尝试关闭流时出现的错误。
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();
}
}
using (var sw = webRequest.GetRequestStream())
那么finally语句块会捕获异常,试图关闭它认为已经打开的连接,因为webRequest已经被创建。
如果finally语句块内没有try-catch语句,这段代码将在清理webRequest时引发未处理的异常。
if (webRequest.GetRequestStream() != null)
代码从那里退出时,没有正确处理发生的错误,因此会对调用方法造成问题。
希望这个例子能够帮助你。
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");
}
}
}
在另一个异常仍然存在时抛出异常,会导致第一个异常被第二个(后面的)异常替换。
以下是演示这种情况的代码:
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);
}
}
几个月前,我也遇到了类似的问题。
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);
}
但如果你想要使用参数和返回类型,那就是另外一回事了。
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
处理 CodeA 和 CodeB 抛出的异常的方式是相同的。
finally
块中抛出的异常没有什么特殊之处,将其视为代码 B 抛出的异常即可。