如果finally块抛出异常,完全会发生什么?
具体来说,如果在finally块的中途抛出异常会发生什么。是否调用了此块中的其余语句(之后)?
我知道异常会向上传播。
答案 0 :(得分:391)
如果finally块抛出异常完全会发生什么?
该异常传播出来,并且(可以)在更高级别处理。
你的finally块将不在抛出异常之外完成。
如果在处理早期异常期间执行finally块,则第一个异常将丢失。
C#4语言规范§8.9.5:如果finally块抛出另一个异常,则终止当前异常的处理。
答案 1 :(得分:96)
对于这些问题,我通常在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指出的那样,如果你不在(内部)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
答案 2 :(得分:10)
如果有待处理的异常(当try
块有finally
但没有catch
时),则新异常将替换该异常。
如果没有待处理的异常,它就像在finally
块之外抛出异常一样。
答案 3 :(得分:4)
传播异常。
答案 4 :(得分:2)
我必须这样做才能捕获错误,试图关闭因异常而从未打开过的流。
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())
然后,最终会捕获异常,尝试关闭它认为已打开的连接,因为已创建webRequest。
如果最终内部没有try-catch,则此代码会在清理webRequest时导致未处理的异常
if (webRequest.GetRequestStream() != null)
从那里代码将退出而没有正确处理发生的错误,因此导致调用方法的问题。
希望这有助于作为一个例子
答案 5 :(得分:2)
快速(而且非常明显)代码段保存“原始异常”(抛出try
块)并牺牲“最终异常”(抛出finally
块),以防原始异常更重要为你:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
当执行上面的代码时,“原始异常”会在调用堆栈中向上传播,并且“最终异常”将丢失。
答案 6 :(得分:1)
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
处理CodeA和CodeB引发的异常的方式是相同的。
finally
块中抛出的异常没有什么特别之处,将其视为代码B的异常抛出。
答案 7 :(得分:1)
当另一个异常处于活动状态时抛出异常将导致第一个异常被第二个(稍后的)异常替换。
以下是一些说明会发生什么的代码:
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);
}
}
答案 8 :(得分:1)
几个月前我也遇到过这样的事情,
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);
}
但是如果你想使用参数和返回类型的另一个故事
答案 9 :(得分:1)
异常传播,应该在更高级别处理。如果未在更高级别处理异常,则应用程序崩溃。 &#34;终于&#34;块执行在抛出异常的位置停止。
无论是否存在例外情况&#34;最后&#34;块保证执行。
如果&#34;最后&#34;在try块中发生异常后执行块
如果未处理该例外
如果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");
}
}
}
答案 10 :(得分:-2)
它抛出异常;)您可以在其他一些catch子句中捕获该异常。