例外処理
以下のガイドラインに従うと、ライブラリで例外を適切に処理できます。
System.Exception や System.SystemException などの不特定の例外をフレームワーク コードでキャッチして、エラーを処理しないようにしてください。
例外をキャッチする目的がその例外を再スローしたり、別のスレッドに転送したりすることにある場合は、例外をキャッチできます。 不適切な例外処理を次のコード例に示します。
Public Class BadExceptionHandlingExample1
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
' Handle the exception and
' continue executing.
End Try
End Sub
End Class
public class BadExceptionHandlingExample1
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
// Handle the exception and
// continue executing.
}
}
}
public ref class BadExceptionHandlingExample1
{
public:
void DoWork()
{
// Do some work that might throw exceptions.
}
void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception^ e)
{
// Handle the exception and
// continue executing.
}
}
};
System.Exception や System.SystemException などの不特定の例外をアプリケーション コードでキャッチして、エラーを処理しないようにしてください。 アプリケーションでエラーを処理することが許容される場合もありますが、そのような場合はごくまれです。
アプリケーションでは、予想外の状態やセキュリティ侵害の状態につながる可能性がある例外を処理しないでください。 例外を引き起こしそうなすべての原因を予測できず、さらに結果として生じるアプリケーション状態を悪質なコードが攻略できないことを保証できない場合は、例外を処理する代わりにアプリケーションを終了できるようにする必要があります。
例外を転送するためにキャッチする場合は、特殊な例外を除外しないでください。
catch 句で特殊な例外の一覧を作成する代わりに、正当に処理できる例外だけをキャッチする必要があります。 処理できない例外は、非特定例外ハンドラーで特殊な事例として処理しないでください。 特殊な例外を再スローするときに正しくテストしないコード例を次に示します。
Public Class BadExceptionHandlingExample2
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
If TypeOf e Is StackOverflowException Or _
TypeOf e Is OutOfMemoryException Then
Throw
End If
End Try
End Sub
End Class
public class BadExceptionHandlingExample2
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
if (e is StackOverflowException ||
e is OutOfMemoryException)
throw;
// Handle the exception and
// continue executing.
}
}
}
public ref class BadExceptionHandlingExample2
{
public:
void DoWork()
{
// Do some work that might throw exceptions.
}
void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception^ e)
{
if (e->GetType() == StackOverflowException::typeid ||
e->GetType() == OutOfMemoryException::typeid)
throw;
// Handle the exception and
// continue executing.
}
}
};
特定の例外が特定のコンテキストでスローされる理由を把握できている場合は、その例外をキャッチするようにしてください。
回復可能な例外だけをキャッチする必要があります。 たとえば、存在しないファイルを開こうとした場合に発生する FileNotFoundException は、アプリケーションで処理できる例外です。それは、アプリケーションがユーザーに問題を知らせ、ユーザーが別のファイル名を指定したり、ファイルを作成したりできるようにすることが可能だからです。 ExecutionEngineException を生成するような、ファイルのオープン要求は、例外の根本原因が把握できず、実行を継続することの安全性をアプリケーションが保証できないため、処理しないでください。
catch を過度に使用しないでください。 例外によって呼び出し履歴がいっぱいになることがよくあります。
正当に処理できない例外をキャッチすると、重要なデバッグ情報が隠されます。
クリーンアップ コードでは try-finally を使用し、try-catch の使用は避けてください。 適切に記述された例外コードでは、try-finally の方が try-catch よりもはるかに一般的です。
catch 句の目的は、致命的ではないエラーを記録するなどして、例外を処理できるようにすることです。 finally 句の目的は、例外がスローされたかどうかに関係なくクリーンアップ コードを実行できるようにすることです。 データベース接続やデータベース ストリームなど、負荷が大きいリソースや制限されたリソースを割り当てる場合は、それらを解放するコードを finally ブロック内に配置する必要があります。
例外をキャッチして再スローする場合は、空の throw を使用してください。 例外呼び出し履歴を保持するには、これが最善の方法です。
例外をスローできるメソッドを次のコード例に示します。 このメソッドは、後の例で参照します。
Public Sub DoWork(ByVal anObject As Object)
' Do some work that might throw exceptions.
If (anObject = Nothing) Then
Throw New ArgumentNullException("anObject", "Specify a non-null argument.")
End If
' Do work with o.
End Sub
public void DoWork(Object anObject)
{
// Do some work that might throw exceptions.
if (anObject == null)
{
throw new ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
public:
void DoWork(Object^ anObject)
{
// Do some work that might throw exceptions.
if (anObject == nullptr)
{
throw gcnew ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
例外をキャッチし、キャッチした例外を再スローするときに正しく指定しないコード例を次に示します。 これにより、スタック トレースは、エラー位置として、DoWork メソッドをポイントする代わりに再スローをポイントします。
Public Sub MethodWithBadCatch(ByVal anObject As Object)
Try
DoWork(anObject)
Catch e As ArgumentNullException
System.Diagnostics.Debug.Write(e.Message)
' This is wrong.
Throw e
' Should be this:
' throw
End Try
End Sub
public void MethodWithBadCatch(Object anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException e)
{
System.Diagnostics.Debug.Write(e.Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
void MethodWithBadCatch(Object^ anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException^ e)
{
System::Diagnostics::Debug::Write(e->Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
パラメーターなしの catch ブロックを使用して、非 CLS 準拠の例外 (System.Exception から派生していない例外) を処理しないでください。 Exception から派生していない例外をサポートする言語では、このような非 CLS 準拠の例外を自由に処理できます。
.NET Framework Version 2.0 は、非 CLS 準拠の例外を Exception から派生したクラスにラップします。
Portions Copyright 2005 Microsoft Corporation. All rights reserved.
Portions Copyright Addison-Wesley Corporation. All rights reserved.
設計ガイドラインの詳細についてを参照してください、「フレームワークの設計ガイドライン。規則、慣用句、および再利用可能なパターン。ネット ライブラリ」本クシシュトフ Cwalina、ブラッド エイブラムス、アスキー、2005 年発表しました。