finallyブロックが例外をスローした場合、正確にはどうなりますか?
具体的には、finallyブロックの途中で例外がスローされるとどうなりますか。このブロックの残りのステートメント(後)は呼び出されますか?
例外が上方に伝播することを認識しています。
finallyブロックが例外をスローした場合、正確にはどうなりますか?
具体的には、finallyブロックの途中で例外がスローされるとどうなりますか。このブロックの残りのステートメント(後)は呼び出されますか?
例外が上方に伝播することを認識しています。
回答:
finallyブロックが例外をスローした場合、正確にはどうなりますか?
その例外は外に伝播し、より高いレベルで処理されます(できる)。
最後のブロックは、例外がスローされるポイントを超えて完了しません。
以前の例外の処理中にfinallyブロックが実行されていた場合、その最初の例外は失われます。
C#4言語仕様§8.9.5:finallyブロックが別の例外をスローすると、現在の例外の処理が終了します。
ThreadAbortException
、最後のブロック全体が最初に終了します。これはクリティカルセクションであるためです。
このような質問では、通常、空のコンソールアプリケーションプロジェクトを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は到達不能コードを検出したことを警告します):
tryブロックからスローされた例外を処理する内部catchブロック。 インナー最終ブロック finallyブロックからスローされた例外を処理する外部キャッチブロック。 ついに外側をブロック
追加備考
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");
}
}
}
出力からわかるように、内部例外は「失われています」(つまり無視されています)。
インナー最終ブロック finallyブロックからスローされた例外を処理する外部キャッチブロック。 ついに外側をブロック
finally
ブロックは(ほぼ)常に実行されます。これは、この場合も内部のfinal ブロックに当てはまります(サンプルプログラムを自分で試してみてください(回復不能の場合は、finallyブロックは実行されません)例外、たとえばEngineExecutionException
、ただし、そのような場合、プログラムはとにかくすぐに終了します)
保留中の例外がある場合(try
ブロックにはあるfinally
ががない場合catch
)、新しい例外がその例外を置き換えます。
保留中の例外がない場合、finally
ブロックの外側で例外をスローするのと同じように機能します。
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();
}
}
webRequestが作成されたが、接続エラーが発生した場合
using (var sw = webRequest.GetRequestStream())
次に、最後に、webRequestが作成されたために開いていたと見なした接続をクローズしようとする例外をキャッチします。
最終的に内部にtry-catchがなかった場合、このコードはwebRequestをクリーンアップするときに未処理の例外を発生させます。
if (webRequest.GetRequestStream() != null)
そこから、発生したエラーを適切に処理せずにコードが終了し、呼び出しメソッドに問題が発生します。
これが例として役立つことを願っています
別の例外がアクティブなときに例外をスローすると、最初の例外が2番目の(後で)例外に置き換えられます。
何が起こるかを示すコードを次に示します。
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で例外スローとして扱い、何も特別なを持っています
例外は上に伝播し、より高いレベルで処理する必要があります。例外が上位レベルで処理されない場合、アプリケーションがクラッシュします。「最終的に」ブロックの実行は、例外がスローされた時点で停止します。
例外があるかどうかに関係なく、「最終的に」ブロックの実行が保証されます。
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");
}
}
}