例外処理の実施
適切に記述されたエラー処理コード ブロックを使用すると、プログラムの堅牢性が強化され、アプリケーションでのエラー処理が原因でクラッシュする可能性が低減します。 例外処理の実施に関する推奨事項を次に示します。
try ブロックおよび catch ブロックを設定する状況について理解します。 たとえば、例外処理を実行せずに、発生する可能性のあるエラーの条件をプログラムによってチェックできます。 その他の場合には、例外処理によってエラー条件をキャッチする方法が適切です。
接続が閉じているかどうかをチェックする if ステートメントを次のコード例に示します。 接続が閉じていない場合に例外をスローする方法の代わりに、この例のようにプログラムでチェックする方法を使用できます。
If conn.State <> ConnectionState.Closed Then
conn.Close()
End IF
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
if (conn->State != ConnectionState::Closed)
{
conn->Close();
}
接続が閉じていない場合に例外をスローするコード例を次に示します。
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
try
{
conn->Close();
}
catch (InvalidOperationException^ ex)
{
Console::WriteLine(ex->GetType()->FullName);
Console::WriteLine(ex->Message);
}
例外処理を使用するかどうかは、予期されるイベント発生頻度によって決まります。 イベントが例外的であり、エラー (予期しないファイルの終端の検出など) である場合は、例外処理を使用する方法が有効です。これは、通常の場合は、実行されるコードが少ないためです。 イベントが定期的に発生する場合は、プログラムによってエラーをチェックする方法が適切です。 これは、例外が発生した場合に、例外を処理する時間がかかるためです。
例外が発生する可能性のあるコードの前後に try ブロックと finally ブロックを配置し、catch ステートメントを 1 か所にまとめて配置します。 このように配置することで、try ステートメントで例外が発生すると、finally ステートメントがリソースを閉じるかまたは解放し、catch ステートメントが中央の 1 か所から例外を処理します。
catch ブロックでは、特定の例外から一般的な例外の順に例外を配置します。 この手法により、まず特定の例外が処理され、次にこの例外がより一般的な catch ブロックに渡されます。
例外クラス名の終わりに "Exception" という単語を付けてください。 次に例を示します。
Public Class MyFileNotFoundException
Inherits Exception
End Class
public class MyFileNotFoundException : Exception
{
}
public ref class MyFileNotFoundException : public Exception
{
};
ユーザー定義例外を作成するときには例外のメタデータを使用できるようにしてください。アプリケーション ドメイン間で例外が発生した場合や、リモート処理で必要となります。 たとえば、アプリケーション ドメイン A によってアプリケーション ドメイン B が作成され、このドメイン B で例外をスローするコードが実行されるとします。 アプリケーション ドメイン A で例外を適切にキャッチして処理するには、ドメイン A が、ドメイン B によりスローされた例外が格納されているアセンブリを検出できる必要があります。 アプリケーション ドメイン B がスローした例外が、ドメイン A のアプリケーション ベースではなくドメイン B のアプリケーション ベースにあるアセンブリに格納されている場合、ドメイン A は例外を検出できないため、共通言語ランタイムが FileNotFoundException をスローします。 このような状況を回避するには、例外情報が格納されているアセンブリを次のいずれかの方法で配置できます。
2 つのアプリケーション ドメインが共有する共通アプリケーション ベースにアセンブリを配置する。
または
ドメインが共通アプリケーション ベースを共有していない場合には、例外情報が格納されているアセンブリに厳密な名前で署名し、グローバル アセンブリ キャッシュにこのアセンブリを配置する。
C# と C++ で独自の例外クラスを作成するときには、3 つ以上の共通コンストラクターを使用します。 例については、「方法 : ユーザー定義の例外を作成する」を参照してください。
ほとんどの場合、事前に定義されている例外を使用します。 プログラミング用途に限り、新しい例外の種類を定義します。 新しい例外クラスを導入して、プログラマがこの新しい例外クラスに基づいて異なる処理をコーディングできるようにします。
ほとんどのアプリケーションでは、Exception クラスからカスタム例外を派生します。 本来、カスタム例外は ApplicationException クラスから派生しなければならないと考えられていましたが、実際には、それによって大きな価値が付加されたことはないようです。
すべての例外に、ローカライズした説明文字列を含めます。 ユーザーに対して表示されるエラー メッセージは、例外クラスではなく、スローされた例外の説明文字列から派生されます。
文法的に正しいエラー メッセージを使用します。句点も含めてください。 例外の説明文字列の文の末尾には、必ず句点を使用します。
プログラムからアクセスできるように、Exception のプロパティを設定します。 説明文字列以外の追加情報を例外に含めるのは、プログラミングの点で追加情報が役立つ場合に限定してください。
非常に一般的なエラーの場合には null を返します。 たとえば、Open では、ファイルが見つからない場合は null を返しますが、ファイルがロックされている場合は例外をスローします。
通常の使用状態では例外が返されないようにクラスをデザインします。 たとえば、FileStream クラスでは、ファイルの終端に到達したかどうかを判別するための例外を使用しない方法が公開されています。 これにより、ファイルの終端を越えて読み取りを実行しようとした場合にも例外がスローされません。 ファイルの終端の読み取り方法を示すコード例を次に示します。
Class FileRead
Public Sub ReadAll(fileToRead As FileStream)
' This if statement is optional
' as it is very unlikely that
' the stream would ever be null.
If fileToRead Is Nothing Then
Throw New System.ArgumentNullException()
End If
Dim b As Integer
' Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin)
' Read each byte to the end of the file.
For i As Integer = 0 To fileToRead.Length - 1
b = fileToRead.ReadByte()
Console.Write(b.ToString())
' Or do something else with the byte.
Next i
End Sub
End Class
class FileRead
{
public void ReadAll(FileStream fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == null)
{
throw new System.ArgumentNullException();
}
int b;
// Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}
class FileRead
{
public:
void ReadAll(FileStream^ fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == nullptr)
{
throw gcnew System::ArgumentNullException();
}
int b;
// Set the stream position to the beginning of the file.
fileToRead->Seek(0, SeekOrigin::Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead->Length; i++)
{
b = fileToRead->ReadByte();
Console::Write(b.ToString());
// Or do something else with the byte.
}
}
};
オブジェクトの現在の状態に対して、プロパティ セットまたはメソッドの呼び出しが適切でない場合は、InvalidOperationException をスローします。
無効なパラメーターが渡された場合は、ArgumentException または ArgumentException の派生クラスをスローします。
例外がスローされたステートメントからスタック トレースが開始され、例外をキャッチしたステートメントでトレースが終了します。 throw ステートメントを配置する位置を決定するときには、このことに注意してください。
例外ビルダー メソッドを使用します。 一般に、クラスはクラス実装内の複数の位置で同一の例外をスローします。 コードが長くなることを防ぐため、例外を作成して返すヘルパー メソッドを使用します。 次に例を示します。
Class FileReader
Private fileName As String
Public Sub New(path As String)
fileName = path
End Sub
Public Function Read(bytes As Integer) As Byte()
Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
If results Is Nothing
Throw NewFileIOException()
End If
Return results
End Function
Function NewFileIOException() As FileReaderException
Dim description As String = "My NewFileIOException Description"
Return New FileReaderException(description)
End Function
End Class
class FileReader
{
private string fileName;
public FileReader(string path)
{
fileName = path;
}
public byte[] Read(int bytes)
{
byte[] results = FileUtils.ReadFromFile(fileName, bytes);
if (results == null)
{
throw NewFileIOException();
}
return results;
}
FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
return new FileReaderException(description);
}
}
ref class FileReader
{
private:
String^ fileName;
public:
FileReader(String^ path)
{
fileName = path;
}
array<Byte>^ Read(int bytes)
{
array<Byte>^ results = FileUtils::ReadFromFile(fileName, bytes);
if (results == nullptr)
{
throw NewFileIOException();
}
return results;
}
FileReaderException^ NewFileIOException()
{
String^ description = "My NewFileIOException Description";
return gcnew FileReaderException(description);
}
};
例外のコンストラクターを使用して例外を作成することもできます。 この方法は、ArgumentException などのグローバル例外クラスに適しています。
エラー コードや HRESULT を返す代わりに、例外をスローします。
例外をスローするときに、中間結果を削除します。 呼び出し元が、メソッドから例外がスローされるときに副作用が発生しないと仮定できる必要があります。