如果finally块抛出异常会发生什么?

如果finally块抛出异常,会发生什么完全 ?

具体来说,如果在finally块中途抛出异常会发生什么。这个块中的其余语句(之后)是否被调用?

我知道异常会向上传播。

105560 次浏览

传播异常。

它抛出一个异常;)您可以在其他catch子句中捕获该异常。

如果finally块抛出异常,会发生什么完全 ?

该异常向外和向上传播,并且将(可以)在更高的级别进行处理。

finally块将在抛出异常之后完成

如果finally块是在处理之前的异常期间执行的,那么第一个异常将丢失。

c# 4语言规范和章节;8.9.5:如果finally块抛出另一个异常,则当前异常的处理将终止。

public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}

CodeA和CodeB抛出的异常的处理方式是相同的。

finally块中抛出的异常没有任何特殊含义,将其视为代码B抛出的异常。

对于这样的问题,我通常在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");
}
}
}

当你运行程序时,你会看到catchfinally块执行的确切顺序。请注意,在抛出异常后,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指出的那样,try块中的异常如果不在(内部)catch块中处理,就会被“吃掉”。事实上,在上面的例子中,重新抛出的异常并没有出现在外层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

如果有一个异常挂起(当try块有finally但没有catch时),新异常将替换该异常。

如果没有异常挂起,它就像在finally块外抛出异常一样工作。

在另一个异常活动时抛出异常将导致第一个异常被第二个(稍后)异常所取代。

下面是一些代码,说明发生了什么:

    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语句的注释,您将看到“第一个异常”
  • 也取消注释抛出;语句,您将再次看到“第二个异常”。

几个月前,我也遇到过这样的事情,

    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);
}

但如果你想使用参数并返回类型,那就是另一回事了

我必须这样做是为了捕捉一个错误,试图关闭一个从未因为异常而打开的流。

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)

从那里,代码将退出,而没有正确处理所发生的错误,因此会导致调用方法出现问题。

希望这能起到示范作用

保存“原始异常”(在try块中抛出)并牺牲“最终异常”(在finally块中抛出)的快速(相当明显)代码片段,以防原始异常对你更重要:

try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}

当执行上面的代码时,“原始异常”向上传播调用堆栈,“最终异常”丢失。

异常向上传播,应该在更高的级别进行处理。如果不在更高的级别处理异常,应用程序将崩溃。“finally”块执行在抛出异常的地方停止。

无论是否存在异常,“finally”块都保证执行。

  1. 如果“finally”块在try块中发生异常后被执行,

  2. 如果这个异常没有被处理

  3. 如果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");
}
}
}

详细信息的伟大文章