System.Exception クラス
この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。
このクラスは Exception 、すべての例外の基底クラスです。 エラーが発生すると、システムまたは現在実行中のアプリケーションのいずれかが、エラーに関する情報を含む例外をスローして報告します。 例外がスローされると、アプリケーションまたは既定の例外ハンドラーによって処理されます。
エラーと例外
さまざまな理由で実行時エラーが発生する可能性があります。 ただし、すべてのエラーをコードの例外として処理する必要はありません。 実行時に発生する可能性があるエラーのカテゴリと、それらに対応する適切な方法を次に示します。
使用状況エラー。 使用状況エラーは、例外が発生する可能性があるプログラム ロジックのエラーを表します。 ただし、エラーは、例外処理ではなく、問題のあるコードを変更して処理する必要があります。 たとえば、次の例の Object.Equals(Object) メソッドのオーバーライドでは、
obj
引数は常に null 以外である必要があることを前提としています。using System; public class Person1 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation contains an error in program logic: // It assumes that the obj argument is not null. Person1 p = (Person1) obj; return this.Name.Equals(p.Name); } } public class UsageErrorsEx1 { public static void Main() { Person1 p1 = new Person1(); p1.Name = "John"; Person1 p2 = null; // The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); } }
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation contains an error in program logic: // It assumes that the obj argument is not null. let p = obj :?> Person this.Name.Equals p.Name let p1 = Person() p1.Name <- "John" let p2: Person = null // The following throws a NullReferenceException. printfn $"p1 = p2: {p1.Equals p2}"
Public Class Person Private _name As String Public Property Name As String Get Return _name End Get Set _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation contains an error in program logic: ' It assumes that the obj argument is not null. Dim p As Person = CType(obj, Person) Return Me.Name.Equals(p.Name) End Function End Class Module Example2 Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing ' The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module
Object.Equals オーバーライドを呼び出してから再コンパイルする前に null を明示的にテストするようにソース コードを変更することで、NullReferenceException 例外では
obj
がnull
である場合に排除できます。 次の例には、null
引数を処理する修正されたソース コードが含まれています。using System; public class Person2 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation handles a null obj argument. Person2 p = obj as Person2; if (p == null) return false; else return this.Name.Equals(p.Name); } } public class UsageErrorsEx2 { public static void Main() { Person2 p1 = new Person2(); p1.Name = "John"; Person2 p2 = null; Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); } } // The example displays the following output: // p1 = p2: False
// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation handles a null obj argument. match obj with | :? Person as p -> this.Name.Equals p.Name | _ -> false let p1 = Person() p1.Name <- "John" let p2: Person = null printfn $"p1 = p2: {p1.Equals p2}" // The example displays the following output: // p1 = p2: False
Public Class Person2 Private _name As String Public Property Name As String Get Return _name End Get Set _name = Value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation handles a null obj argument. Dim p As Person2 = TryCast(obj, Person2) If p Is Nothing Then Return False Else Return Me.Name.Equals(p.Name) End If End Function End Class Module Example3 Public Sub Main() Dim p1 As New Person2() p1.Name = "John" Dim p2 As Person2 = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: False
使用状況エラーに例外処理を使用する代わりに、Debug.Assert メソッドを使用してデバッグ ビルドの使用状況エラーを特定し、Trace.Assert メソッドを使用してデバッグ ビルドとリリース ビルドの両方で使用状況エラーを特定できます。 詳細については、「マネージド コードのアサーション」を参照してください。
プログラムのエラー。 プログラムのエラーは、バグのないコードを記述することによって必ずしも回避できない実行時エラーです。
場合によっては、プログラム エラーに予期されるエラーまたはルーチン エラーの状態が反映される可能性があります。 この場合は、プログラム エラーに対処するために例外処理を使用しないようにし、代わりに操作を再試行することをお勧めします。 たとえば、ユーザーが特定の形式で日付を入力することが予想される場合は、DateTime.TryParseExact メソッドを呼び出して日付文字列を解析できます。このメソッドは、DateTime.ParseExact メソッドを使用する代わりに解析操作が成功したかどうかを示す値 Boolean を返します。このメソッドは、日付文字列を DateTime 値に変換できない場合に FormatException 例外をスローします。 同様に、ユーザーが存在しないファイルを開こうとした場合は、まず File.Exists メソッドを呼び出してファイルが存在するかどうかをチェックし、存在しない場合は、作成するかどうかをユーザーに求めることができます。
それ以外の場合、プログラム エラーには、コードで処理できる予期しないエラー状態が反映されます。 たとえば、ファイルが存在することをチェックする場合でも、ファイルを開く前に削除されたり、破損したりする可能性があります。 その場合、StreamReader オブジェクトをインスタンス化するか、Open メソッドを呼び出してファイルを開こうとすると、FileNotFoundException 例外がスローされる可能性があります。 このような場合は、例外処理を使用してエラーから復旧する必要があります。
システム障害。 システム障害は、プログラムを使用して意味のある方法で処理することはできない実行時エラーです。 たとえば、共通言語ランタイムが追加のメモリを割り当てることができない場合、どのメソッドでも OutOfMemoryException 例外をスローできます。 通常、システム障害は例外処理を使用して処理されません。 代わりに、AppDomain.UnhandledException などのイベントを使用し、Environment.FailFast メソッドを呼び出して例外情報をログに記録し、アプリケーションが終了する前に障害をユーザーに通知することができます。
try ブロックと catch ブロック
共通言語ランタイムには、例外をオブジェクトとして表現し、プログラム コードと例外処理コードを try
ブロックと catch
ブロックに分離した例外処理モデルが用意されています。 それぞれが特定の種類の例外を処理するように設計されている 1 つ以上の catch
ブロック、または別のブロックよりも特定の例外をキャッチするように設計された 1 つのブロックがあります。
アプリケーション コードのブロックを実行している際に発生する例外をアプリケーションが処理する場合、コードは try
ステートメント内に位置する必要があり、try
ブロックと呼ばれます。 try
ブロックによってスローされる例外を処理するアプリケーション コードは、catch
ステートメント内に位置し、catch
ブロックと呼ばれます。 0 個以上の catch
ブロックが try
ブロックに関連付けられます。各 catch
ブロックには、処理する例外の種類を決定する型フィルターが含まれています。
try
ブロックで例外が発生すると、システムは、例外を処理する catch
ブロックが見つかるまで、アプリケーション コードに表示される順序で関連する catch
ブロックを検索します。 catch ブロックの型フィルターで T
が指定されている場合、または T
から派生した型である場合、catch
ブロックは型 T
の例外を処理します。 例外を処理する最初の catch
ブロックが見つかると、システムは検索を停止します。 このため、アプリケーション コードでは、このセクションの後の例で示すように、型を処理する catch
ブロックを、その基本データ型を処理する catch
ブロックの前に指定する必要があります。 System.Exception
をハンドルする catch ブロックは最後に指定されます。
現在 try
ブロックに関連付けられている catch
ブロックのいずれも例外を処理しない場合、現在の try
ブロックが現在の呼び出しの他の try
ブロック内で入れ子になっている場合は、次の外側の try
ブロックに関連付けられている catch
ブロックが検索されます。 例外の catch
ブロックが見つからない場合、システムは現在の呼び出しで以前の入れ子レベルを検索します。 現在の呼び出しで例外の catch
ブロックが見つからない場合、例外は呼び出し履歴に渡され、前のスタック フレームで例外を処理する catch
ブロックが検索されます。 呼び出し履歴の検索は、例外が処理されるまで、または呼び出し履歴にこれ以上フレームが存在しなくなるまで続行されます。 例外を処理する catch
ブロックが見つからずに呼び出し履歴の先頭に到達すると、既定の例外ハンドラーによって処理され、アプリケーションが終了します。
F# try..with 式
F# では catch
ブロックは使用されません。 代わりに、発生した例外は、単一の with
ブロックを使用してパターン一致します。 これはステートメントではなく式であるため、すべてのパスは同じ型を返す必要があります。 詳細については、try...with 式を参照してください。
例外の種類の機能
例外の種類では、次の機能がサポートされています。
エラーを説明する、人間が判読できるテキスト。 例外が発生すると、ランタイムは、エラーの性質をユーザーに通知し、問題を解決するためのアクションを提案するテキスト メッセージを使用できるようにします。 このテキスト メッセージは、例外オブジェクトの Message プロパティに保持されます。 例外オブジェクトの作成時に、テキスト文字列をコンストラクターに渡して、その特定の例外の詳細を記述できます。 コンストラクターにエラー メッセージ引数が指定されていない場合は、既定のエラー メッセージが使用されます。 詳細については、Message プロパティを参照してください。
例外がスローされたときの呼び出し履歴の状態。 StackTrace プロパティは、コードでエラーが発生した場所を判断するために使用できるスタック トレースを持ち込みます。 スタック トレースには、呼び出されたすべてのメソッドと、呼び出しが行われるソース ファイル内の行番号が一覧表示されます。
Exception クラス プロパティ
Exception クラスには、コードの場所、型、ヘルプ ファイル、および例外の理由を識別するのに役立つ次のような多数のプロパティが含まれています。StackTrace、InnerException、Message、HelpLink、HResult、Source、TargetSite、および Data。
2 つ以上の例外の間に因果関係が存在する場合、InnerException プロパティはこの情報を保持します。 外側の例外は、この内部例外に応答してスローされます。 外側の例外を処理するコードでは、以前の内部例外からの情報を使用して、エラーをより適切に処理できます。 例外に関する補足情報は、キーと値のペアのコレクションとして Data プロパティに格納できます。
例外オブジェクトの作成時にコンストラクターに渡されるエラー メッセージ文字列はローカライズする必要があり、ResourceManager クラスを使用してリソース ファイルから指定できます。 ローカライズされたリソースの詳細については、「サテライト アセンブリの作成」と「リソースのパッケージ化とデプロイ」に関するトピックを参照してください。
例外が発生した理由に関する詳細な情報をユーザーに提供するために、HelpLink プロパティはヘルプ ファイルへの URL (または URN) を保持できます。
Exception クラスは、値 0x80131500 を持つ HRESULT COR_E_EXCEPTION
を使用します。
Exception クラスのインスタンスの初期プロパティ値の一覧については、Exception コンストラクターに関するトピックを参照してください。
パフォーマンスに関する考慮事項
例外をスローまたは処理すると、大量のシステム リソースと実行時間が消費されます。 例外は、予測可能なイベントやフロー制御を処理するためではなく、本当に特別な条件を処理するためだけにスローされます。 たとえば、クラス ライブラリを開発する場合など、メソッド引数が無効である場合は、メソッドが有効なパラメーターで呼び出されることを想定しているため、例外をスローするのが妥当である場合があります。 無効なメソッド引数が使用状況エラーの結果でない場合は、何らかの異常が発生したことを意味します。 逆に、ユーザー入力が無効である場合は、ユーザーが無効なデータを入力する場合があるため、例外をスローしないでください。 代わりに、ユーザーが有効な入力を入力できるように再試行のメカニズムを指定します。 また、使用状況エラーを処理するために例外を使用する必要もありません。 代わりに、アサーションを使用して、使用状況エラーを特定して修正します。
また、リターン コードで十分な場合は例外をスローしないでください。また、リターン コードを例外に変換しないでください。例外を定期的にキャッチせず、無視してから処理を続行します。
例外を再スローする
多くの場合、例外ハンドラーは単に例外を呼び出し元に渡す必要があります。 これは、ほとんどの場合、次のような場合に発生します。
.NET クラス ライブラリまたはその他のクラス ライブラリ内のメソッドの呼び出しをラップするクラス ライブラリ。
致命的な例外が発生したアプリケーションまたはライブラリ。 例外ハンドラーは、例外をログに記録してから、例外を再スローできます。
例外を再スローするために推奨される方法は、C# の throw ステートメント、F# の reraise 関数、および Visual Basic の Throw ステートメントを式を含めずに使用することです。 これにより、例外が呼び出し元に伝達されるときに、すべての呼び出し履歴情報が保持されます。 次の例は、これを示しています。 文字列拡張メソッド (FindOccurrences
) は、引数を事前に検証せずに、1 つ以上の呼び出しを String.IndexOf(String, Int32) にラップします。
using System;
using System.Collections.Generic;
public static class Library1
{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f, currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some action here, such as logging this exception.
throw;
}
return indexes.ToArray();
}
}
open System
module Library =
let findOccurrences (s: string) (f: string) =
let indexes = ResizeArray()
let mutable currentIndex = 0
try
while currentIndex >= 0 && currentIndex < s.Length do
currentIndex <- s.IndexOf(f, currentIndex)
if currentIndex >= 0 then
indexes.Add currentIndex
currentIndex <- currentIndex + 1
with :? ArgumentNullException ->
// Perform some action here, such as logging this exception.
reraise ()
indexes.ToArray()
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Public Module Library
<Extension()>
Public Function FindOccurrences1(s As String, f As String) As Integer()
Dim indexes As New List(Of Integer)
Dim currentIndex As Integer = 0
Try
Do While currentIndex >= 0 And currentIndex < s.Length
currentIndex = s.IndexOf(f, currentIndex)
If currentIndex >= 0 Then
indexes.Add(currentIndex)
currentIndex += 1
End If
Loop
Catch e As ArgumentNullException
' Perform some action here, such as logging this exception.
Throw
End Try
Return indexes.ToArray()
End Function
End Module
呼び出し元は FindOccurrences
を 2 回呼び出します。 FindOccurrences
に対する 2 番目の呼び出しでは、呼び出し元は検索文字列として null
を渡します。これにより、String.IndexOf(String, Int32) メソッドは ArgumentNullException 例外をスローします。 この例外は FindOccurrences
メソッドによって処理され、呼び出し元に返されます。 throw ステートメントは式なしで使用されるため、この例からの出力は呼び出し履歴が保持されていることを示しています。
public class RethrowEx1
{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();
String toFind = null;
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine("Message:\n {0}\n", e.Message);
Console.WriteLine("Stack Trace:\n {0}\n", e.StackTrace);
}
}
private static void ShowOccurrences(String s, String toFind, int[] indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr = 0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length - 1 ? "" : ", ");
Console.WriteLine();
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
open Library
let showOccurrences toFind (indexes: int[]) =
printf $"'{toFind}' occurs at the following character positions: "
for i = 0 to indexes.Length - 1 do
printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}"""
printfn ""
let s = "It was a cold day when..."
let indexes = findOccurrences s "a"
showOccurrences "a" indexes
printfn ""
let toFind: string = null
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $"Message:\n {e.Message}\n"
printfn $"Stack Trace:\n {e.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null. (Parameter 'value')
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.findOccurrences(String s, String f)
// at <StartupCode$fs>.main@()
Module Example1
Public Sub Main()
Dim s As String = "It was a cold day when..."
Dim indexes() As Integer = s.FindOccurrences1("a")
ShowOccurrences(s, "a", indexes)
Console.WriteLine()
Dim toFind As String = Nothing
Try
indexes = s.FindOccurrences1(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine("Message:{0} {1}{0}", vbCrLf, e.Message)
Console.WriteLine("Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
End Try
End Sub
Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer())
Console.Write("'{0}' occurs at the following character positions: ",
toFind)
For ctr As Integer = 0 To indexes.Length - 1
Console.Write("{0}{1}", indexes(ctr),
If(ctr = indexes.Length - 1, "", ", "))
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message:
' Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
これに対し、このステートメントを使用して例外が再スローされる場合は、次のようになります。
throw e;
Throw e
raise e
...その後、完全な呼び出し履歴は保持されず、この例では次のような出力が生成されます。
'a' occurs at the following character positions: 4, 7, 15
An exception (ArgumentNullException) occurred.
Message:
Value cannot be null.
Parameter name: value
Stack Trace:
at Library.FindOccurrences(String s, String f)
at Example.Main()
もう少し面倒な方法は、新しい例外をスローし、元の例外の呼び出し履歴情報を内部例外に保持することです。 呼び出し元は、新しい例外の InnerException プロパティを使用して、スタック フレームとその他の元の例外に関する情報を取得できます。 この場合は、throw ステートメントは次のようになります。
throw new ArgumentNullException("You must supply a search string.", e);
raise (ArgumentNullException("You must supply a search string.", e) )
Throw New ArgumentNullException("You must supply a search string.",
e)
次の例外ハンドラーが示すように、例外を処理するユーザー コードでは、InnerException プロパティに元の例外に関する情報が含まれていることを認識する必要があります。
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine(" Message:\n{0}", e.Message);
Console.WriteLine(" Stack Trace:\n {0}", e.StackTrace);
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name);
Console.WriteLine(" Message: {0}\n", ie.Message);
Console.WriteLine(" Stack Trace:\n {0}\n", ie.StackTrace);
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $" Message:\n{e.Message}"
printfn $" Stack Trace:\n {e.StackTrace}"
let ie = e.InnerException
if ie <> null then
printfn " The Inner Exception:"
printfn $" Exception Name: {ie.GetType().Name}"
printfn $" Message: {ie.Message}\n"
printfn $" Stack Trace:\n {ie.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
Try
indexes = s.FindOccurrences(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, e.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
Dim ie As Exception = e.InnerException
If ie IsNot Nothing Then
Console.WriteLine(" The Inner Exception:")
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, ie.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, ie.StackTrace)
End If
End Try
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message: You must supply a search string.
'
' Stack Trace:
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
'
' The Inner Exception:
' Exception Name: ArgumentNullException
' Message: Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
標準の例外を選択する
例外をスローする必要がある場合は、多くの場合、カスタム例外を実装する代わりに、.NET で既存の例外の種類を使用できます。 次の 2 つの条件で標準の例外の種類を使用する必要があります。
使用状況エラー (つまり、メソッドを呼び出している開発者が行ったプログラム ロジックのエラーによるもの) によって発生する例外をスローしています。 通常は、ArgumentException、ArgumentNullException、InvalidOperationException、NotSupportedException などの例外をスローします。 例外オブジェクトをインスタンス化するときに例外オブジェクトのコンストラクターに指定する文字列は、開発者が修正できるようにエラーを記述する必要があります。 詳細については、Message プロパティを参照してください。
既存の .NET 例外を使用して呼び出し元に通知できるエラーを処理しています。 可能な限り最も派生した例外をスローする必要があります。 たとえば、メソッドで引数が列挙型の有効なメンバーである必要がある場合は、ArgumentException ではなく InvalidEnumArgumentException (最も派生したクラス) をスローする必要があります。
次の表に、一般的な例外の種類と、それらをスローする条件を一覧表示します。
例外 | 条件 |
---|---|
ArgumentException | メソッドに渡される null 以外の引数は無効です。 |
ArgumentNullException | メソッドに渡される引数は null です。 |
ArgumentOutOfRangeException | 引数が有効な値の範囲外です。 |
DirectoryNotFoundException | ディレクトリ パスの一部が無効です。 |
DivideByZeroException | 整数演算または Decimal 除算演算の分母は 0 です。 |
DriveNotFoundException | ドライブが利用できないか存在していません。 |
FileNotFoundException | ファイルが存在しません。 |
FormatException | 値は、Parse などの変換メソッドによって文字列から変換される適切な形式ではありません。 |
IndexOutOfRangeException | インデックスが配列またはコレクションの範囲外です。 |
InvalidOperationException | オブジェクトの現在の状態では、メソッド呼び出しが無効です。 |
KeyNotFoundException | コレクション内のメンバーにアクセスするための指定されたキーが見つかりません。 |
NotImplementedException | メソッドまたは操作が実装されていません。 |
NotSupportedException | メソッドまたは操作はサポートされていません。 |
ObjectDisposedException | 破棄されたオブジェクトで操作が実行されました。 |
OverflowException | 算術演算、キャスト演算、または変換操作を実行すると、オーバーフローが発生します。 |
PathTooLongException | パス名またはファイル名がシステム定義の最大長を超えています。 |
PlatformNotSupportedException | この操作は、現在のプラットフォームではサポートされていません。 |
RankException | 次元数に誤りのある配列は、メソッドに渡されます。 |
TimeoutException | 操作に割り当てられた期間の有効期限が切れています。 |
UriFormatException | 有効な Uniform Resource Identifier (URI) が使用されています。 |
カスタム例外を実装する
次の場合、既存の .NET 例外を使用してエラー状態を処理することは適切ではありません。
例外が、既存の .NET 例外にマップできない一意のプログラム エラーを反映している場合。
例外で、既存の .NET 例外に適した処理とは異なる処理が必要な場合、または例外を同様の例外から明確に区別する必要がある場合。 たとえば、対象の整数型の範囲外にある文字列の数値表現を解析するときに ArgumentOutOfRangeException 例外をスローする場合、呼び出し元がメソッドを呼び出すときに適切な制約される値を指定しなかった結果のエラーに対して同じ例外を使用しないようにします。
Exception クラスは、.NET のすべての例外の基底クラスです。 多くの派生クラスは、Exception クラスのメンバーの継承された動作に依存します。ここでは、Exception のメンバーをオーバーライドしたり、一意のメンバーを定義したりすることはありません。
独自の例外クラスを定義するには、次の操作を行います。
Exception を継承するクラスを作成します。 必要に応じて、例外に関する追加情報を提供するためにクラスに必要な一意のメンバーを定義します。 たとえば、ArgumentException クラスには、例外の原因になった引数を持つパラメーターの名前を指定する ParamName プロパティが含まれており、RegexMatchTimeoutException プロパティにはタイムアウト間隔を示す MatchTimeout プロパティが含まれています。
必要に応じて、機能を変更する継承メンバーをオーバーライドします。 ほとんどの Exception の既存の派生クラスは、継承メンバーの動作をオーバーライドしないことに注意してください。
カスタム例外オブジェクトがシリアル化できるかどうかを判断します。 シリアル化を使用すると、例外に関する情報を保存でき、リモート処理コンテキストでサーバーとクライアント プロキシによって例外情報を共有できます。 例外オブジェクトをシリアル化できるようにするには、SerializableAttribute 属性でマークします。
例外クラスのコンストラクターを定義します。 通常、例外クラスには次のコンストラクターが 1 つ以上含まれます。
Exception() は、既定値を使用して新しい例外オブジェクトのプロパティを初期化します。
Exception(String) は、指定したエラー メッセージを使用して新しい例外オブジェクトを初期化します。
Exception(String, Exception) は、指定したエラー メッセージと内部例外を使用して新しい例外オブジェクトを初期化します。
Exception(SerializationInfo, StreamingContext) は、シリアル化されたデータから新しい例外オブジェクトを初期化する
protected
コンストラクターです。 例外オブジェクトをシリアル化できるようにすることを選択した場合は、このコンストラクターを実装する必要があります。
カスタム例外クラスの使用例を次に示します。 これにより、クライアントが素数ではない開始番号を指定して素数のシーケンスを取得しようとしたときにスローされる NotPrimeException
例外が定義されます。 例外は、例外の原因になった非素数を返す新しいプロパティ (NonPrime
) を定義します。 保護されたパラメーターなしのコンストラクターと、シリアル化のための SerializationInfo パラメーターと StreamingContext パラメーターを持つコンストラクターを実装するだけでなく、NotPrimeException
クラスは、NonPrime
プロパティをサポートする 3 つの追加コンストラクターを定義します。 各コンストラクターは、素数以外の値を保持するだけでなく、基底クラスのコンストラクターを呼び出します。 NotPrimeException
クラスは SerializableAttribute 属性でもマークされています。
using System;
using System.Runtime.Serialization;
[Serializable()]
public class NotPrimeException : Exception
{
private int notAPrime;
protected NotPrimeException()
: base()
{ }
public NotPrimeException(int value) :
base(String.Format("{0} is not a prime number.", value))
{
notAPrime = value;
}
public NotPrimeException(int value, string message)
: base(message)
{
notAPrime = value;
}
public NotPrimeException(int value, string message, Exception innerException) :
base(message, innerException)
{
notAPrime = value;
}
protected NotPrimeException(SerializationInfo info,
StreamingContext context)
: base(info, context)
{ }
public int NonPrime
{ get { return notAPrime; } }
}
namespace global
open System
open System.Runtime.Serialization
[<Serializable>]
type NotPrimeException =
inherit Exception
val notAPrime: int
member this.NonPrime =
this.notAPrime
new (value) =
{ inherit Exception($"%i{value} is not a prime number."); notAPrime = value }
new (value, message) =
{ inherit Exception(message); notAPrime = value }
new (value, message, innerException: Exception) =
{ inherit Exception(message, innerException); notAPrime = value }
// F# does not support protected members
new () =
{ inherit Exception(); notAPrime = 0 }
new (info: SerializationInfo, context: StreamingContext) =
{ inherit Exception(info, context); notAPrime = 0 }
Imports System.Runtime.Serialization
<Serializable()> _
Public Class NotPrimeException : Inherits Exception
Private notAPrime As Integer
Protected Sub New()
MyBase.New()
End Sub
Public Sub New(value As Integer)
MyBase.New(String.Format("{0} is not a prime number.", value))
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String)
MyBase.New(message)
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String, innerException As Exception)
MyBase.New(message, innerException)
notAPrime = value
End Sub
Protected Sub New(info As SerializationInfo,
context As StreamingContext)
MyBase.New(info, context)
End Sub
Public ReadOnly Property NonPrime As Integer
Get
Return notAPrime
End Get
End Property
End Class
次の例に示す PrimeNumberGenerator
クラスは、エラトステネスの篩を使用して、そのクラス コンストラクターの呼び出しでクライアントによって指定された 2 から上限までの素数のシーケンスを計算します。 GetPrimesFrom
メソッドは、指定した下限以上のすべての素数を返しますが、その下限が素数でない場合は NotPrimeException
をスローします。
using System;
using System.Collections.Generic;
[Serializable]
public class PrimeNumberGenerator
{
private const int START = 2;
private int maxUpperBound = 10000000;
private int upperBound;
private bool[] primeTable;
private List<int> primes = new List<int>();
public PrimeNumberGenerator(int upperBound)
{
if (upperBound > maxUpperBound)
{
string message = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound);
throw new ArgumentOutOfRangeException(message);
}
this.upperBound = upperBound;
// Create array and mark 0, 1 as not prime (True).
primeTable = new bool[upperBound + 1];
primeTable[0] = true;
primeTable[1] = true;
// Use Sieve of Eratosthenes to determine prime numbers.
for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
ctr++)
{
if (primeTable[ctr]) continue;
for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++)
if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true;
}
// Populate array with prime number information.
int index = START;
while (index != -1)
{
index = Array.FindIndex(primeTable, index, (flag) => !flag);
if (index >= 1)
{
primes.Add(index);
index++;
}
}
}
public int[] GetAllPrimes()
{
return primes.ToArray();
}
public int[] GetPrimesFrom(int prime)
{
int start = primes.FindIndex((value) => value == prime);
if (start < 0)
throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime));
else
return primes.FindAll((value) => value >= prime).ToArray();
}
}
namespace global
open System
[<Serializable>]
type PrimeNumberGenerator(upperBound) =
let start = 2
let maxUpperBound = 10000000
let primes = ResizeArray()
let primeTable =
upperBound + 1
|> Array.zeroCreate<bool>
do
if upperBound > maxUpperBound then
let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}."
raise (ArgumentOutOfRangeException message)
// Create array and mark 0, 1 as not prime (True).
primeTable[0] <- true
primeTable[1] <- true
// Use Sieve of Eratosthenes to determine prime numbers.
for i = start to float upperBound |> sqrt |> ceil |> int do
if not primeTable[i] then
for multiplier = i to upperBound / i do
if i * multiplier <= upperBound then
primeTable[i * multiplier] <- true
// Populate array with prime number information.
let mutable index = start
while index <> -1 do
index <- Array.FindIndex(primeTable, index, fun flag -> not flag)
if index >= 1 then
primes.Add index
index <- index + 1
member _.GetAllPrimes() =
primes.ToArray()
member _.GetPrimesFrom(prime) =
let start =
Seq.findIndex ((=) prime) primes
if start < 0 then
raise (NotPrimeException(prime, $"{prime} is not a prime number.") )
else
Seq.filter ((>=) prime) primes
|> Seq.toArray
Imports System.Collections.Generic
<Serializable()> Public Class PrimeNumberGenerator
Private Const START As Integer = 2
Private maxUpperBound As Integer = 10000000
Private upperBound As Integer
Private primeTable() As Boolean
Private primes As New List(Of Integer)
Public Sub New(upperBound As Integer)
If upperBound > maxUpperBound Then
Dim message As String = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound)
Throw New ArgumentOutOfRangeException(message)
End If
Me.upperBound = upperBound
' Create array and mark 0, 1 as not prime (True).
ReDim primeTable(upperBound)
primeTable(0) = True
primeTable(1) = True
' Use Sieve of Eratosthenes to determine prime numbers.
For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound)))
If primeTable(ctr) Then Continue For
For multiplier As Integer = ctr To CInt(upperBound \ ctr)
If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True
Next
Next
' Populate array with prime number information.
Dim index As Integer = START
Do While index <> -1
index = Array.FindIndex(primeTable, index, Function(flag)
Return Not flag
End Function)
If index >= 1 Then
primes.Add(index)
index += 1
End If
Loop
End Sub
Public Function GetAllPrimes() As Integer()
Return primes.ToArray()
End Function
Public Function GetPrimesFrom(prime As Integer) As Integer()
Dim start As Integer = primes.FindIndex(Function(value)
Return value = prime
End Function)
If start < 0 Then
Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime))
Else
Return primes.FindAll(Function(value)
Return value >= prime
End Function).ToArray()
End If
End Function
End Class
次の例では、非素数を持つ GetPrimesFrom
メソッドを 2 回呼び出します。そのうちの 1 つはアプリケーション ドメイン境界を越えて行います。 どちらの場合も、例外がスローされ、クライアント コードで正常に処理されます。
using System;
using System.Reflection;
class Example1
{
public static void Main()
{
int limit = 10000000;
PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
int start = 1000001;
try
{
int[] values = primes.GetPrimesFrom(start);
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit);
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}
AppDomain domain = AppDomain.CreateDomain("Domain2");
PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
typeof(Example).Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
new object[] { 1000000 }, null, null);
try
{
start = 100;
Console.WriteLine(gen.GetPrimesFrom(start));
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}
}
}
open System
open System.Reflection
let limit = 10000000
let primes = PrimeNumberGenerator limit
let start = 1000001
try
let values = primes.GetPrimesFrom start
printfn $"There are {values.Length} prime numbers from {start} to {limit}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
let domain = AppDomain.CreateDomain "Domain2"
let gen =
domain.CreateInstanceAndUnwrap(
typeof<PrimeNumberGenerator>.Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
[| box 1000000 |], null, null)
:?> PrimeNumberGenerator
try
let start = 100
printfn $"{gen.GetPrimesFrom start}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
Imports System.Reflection
Module Example
Sub Main()
Dim limit As Integer = 10000000
Dim primes As New PrimeNumberGenerator(limit)
Dim start As Integer = 1000001
Try
Dim values() As Integer = primes.GetPrimesFrom(start)
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit)
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
Dim domain As AppDomain = AppDomain.CreateDomain("Domain2")
Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap(
GetType(Example).Assembly.FullName,
"PrimeNumberGenerator", True,
BindingFlags.Default, Nothing,
{1000000}, Nothing, Nothing)
Try
start = 100
Console.WriteLine(gen.GetPrimesFrom(start))
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
End Sub
End Module
' The example displays the following output:
' 1000001 is not prime
' NotPrimeException: 1000001 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
' 100 is not prime
' NotPrimeException: 100 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
例
次の例は、ArithmeticException エラーを処理するために定義された catch
(F# の with
) ブロックを示しています。 DivideByZeroException は ArithmeticException の派生元であり、DivideByZeroException エラーに対して明示的に定義された catch
ブロックがないため、この catch
ブロックは DivideByZeroException エラーもキャッチします。
using System;
class ExceptionTestClass
{
public static void Main()
{
int x = 0;
try
{
int y = 100 / x;
}
catch (ArithmeticException e)
{
Console.WriteLine($"ArithmeticException Handler: {e}");
}
catch (Exception e)
{
Console.WriteLine($"Generic Exception Handler: {e}");
}
}
}
/*
This code example produces the following results:
ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
at ExceptionTestClass.Main()
*/
module ExceptionTestModule
open System
let x = 0
try
let y = 100 / x
()
with
| :? ArithmeticException as e ->
printfn $"ArithmeticException Handler: {e}"
| e ->
printfn $"Generic Exception Handler: {e}"
// This code example produces the following results:
// ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
// at <StartupCode$fs>.$ExceptionTestModule.main@()
Class ExceptionTestClass
Public Shared Sub Main()
Dim x As Integer = 0
Try
Dim y As Integer = 100 / x
Catch e As ArithmeticException
Console.WriteLine("ArithmeticException Handler: {0}", e.ToString())
Catch e As Exception
Console.WriteLine("Generic Exception Handler: {0}", e.ToString())
End Try
End Sub
End Class
'
'This code example produces the following results:
'
'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow.
' at ExceptionTestClass.Main()
'
.NET