例外とは、プログラムの実行中に発生する予期しないイベントまたはエラー状態です。入力ミス、計算ミス、リソース不足、外部環境の変化などが原因で発生する可能性があります。オブジェクト指向プログラミング言語では、例外は通常、プログラムの実行中に続行できず、プログラムの終了や予期しない結果を引き起こすエラーを指します。
例外処理の重要性は、プログラムの安定性と信頼性を向上させる機能にあります。実際のアプリケーションのシナリオでは、ファイルが存在しない、ネットワーク接続が中断された、リソースが枯渇したなど、プログラムはさまざまな異常な状況に直面する可能性があります。適切な例外処理が実行されない場合、これらの例外によってプログラムがクラッシュしたり、誤った結果が生成されたりして、ユーザー エクスペリエンスやシステムの安定性に重大な影響を与える可能性があります。合理的な例外処理を通じて、例外が発生したときに、わかりやすいエラー プロンプトを表示したり、エラーをログに記録したり、例外を修正したり、プログラムを正常に終了したりするなど、対応する措置を講じることができます。これにより、プログラムの異常終了が防止され、プログラムの耐障害性が向上し、システムが異常状態から保護されます。適切な例外処理は、プログラムの安定性と信頼性を高めるだけでなく、問題をより適切に特定して解決するのにも役立ちます。例外をキャッチし、詳細なエラー ログを実行することにより、開発者はエラーのトラブルシューティングとデバッグをより簡単に行うことができるため、開発の効率と品質が向上します。
1. C# 例外処理メカニズム
1.1 例外クラスの継承構造
C# では、例外処理は例外クラスの継承構造を通じて実装されます。すべての例外クラスは、例外クラスの継承構造の基礎である System.Exception クラスから派生します。System.Exception クラスは、派生例外クラスで使用するいくつかの基本的なプロパティとメソッドを定義します。
C# の例外クラスの継承構造は次のとおりです。
- System.Exception: すべての例外クラスの基本クラスであり、メッセージ、スタック トレースなどの例外の基本情報が含まれます。これには 2 つの主要な派生クラスがあります。
- System.SystemException: システムによって定義された例外クラスの基本クラスであり、通常はシステムによってスローされます。
- System.ApplicationException: これはユーザー定義の例外クラスの基本クラスであり、通常はアプリケーションによってスローされます。
- System.SystemException から派生したいくつかの一般的な例外クラス:
- System.NullReferenceException: Null オブジェクトのメンバーにアクセスしようとするとスローされる例外。
- System.IndexOutOfRangeException: 配列またはコレクションに存在しないインデックスにアクセスしようとすると例外がスローされます。
- System.DividedByZeroException: 除数がゼロの場合にスローされる例外。
- System.ArithmeticException: 算術演算例外の基本クラス。
- System.ApplicationException から派生したユーザー定義の例外クラス:
ユーザーは、必要に応じて System.ApplicationException クラスから派生して独自の例外タイプを定義し、さまざまなタイプの例外をより適切に区別できます。
例外クラスの継承構造を使用すると、開発者は、特定の例外状況に応じて、さまざまなタイプの例外をキャッチして処理することを選択できます。一般に、システムによってスローされる例外は System.SystemException クラスから派生した例外ですが、ユーザー定義の例外は通常、System.ApplicationException クラスから派生した例外です。例外をキャッチするときは、ログ記録、わかりやすいエラー プロンプトの表示、再試行など、例外のタイプに応じてさまざまな処理ロジックを実行できます。例外クラスの継承構造により、例外処理がより柔軟かつカスタマイズ可能になり、プログラムの耐障害性と保守性が向上します。
1.2 try-catch ブロック
C# では、try-catch ブロックは例外処理の重要な構成要素です。try-catch ブロックを使用すると、発生する可能性のある例外をキャッチして処理するコードを作成できるため、プログラムのクラッシュや予期しない結果を回避できます。try-catch ブロックの基本的な構文は次のとおりです。
try
{
// 可能会抛出异常的代码
}
catch (ExceptionType1 ex1)
{
// 处理 ExceptionType1 类型的异常
}
catch (ExceptionType2 ex2)
{
// 处理 ExceptionType2 类型的异常
}
// 可以有多个 catch 块,用于处理不同类型的异常
catch (Exception ex)
{
// 处理其他类型的异常
}
finally
{
// 可选的 finally 块,无论是否发生异常,都会执行其中的代码
}
try ブロックでは、例外をスローする可能性のあるコードを記述します。try ブロック内のコードで例外が発生した場合、プログラムは catch ブロックにジャンプし、例外の種類に応じて対応する catch ブロックを照合して例外を処理します。ログ記録、ユーザーフレンドリーなエラープロンプトの表示など、例外処理ロジックを catch ブロックに記述することができます。例外のタイプに一致する catch ブロックがない場合、例外は呼び出しスタックの try-catch ブロックに渡されます。一致する try-catch ブロックがない場合、プログラムはクラッシュします。Finally ブロックはオプションで、try-catch ブロックの終了後に実行され、その中のコードは例外が発生するかどうかに関係なく実行されます。通常、finally ブロックは、リソースを解放したり、ファイルやデータベース接続を閉じるなどのクリーンアップ作業を実行したりするために使用されます。
try-catch ブロックを使用すると、発生する可能性のある例外をキャプチャして処理できるため、プログラムのフォールト トレランスと安定性が向上します。同時に、リソースのリークを避けるために、finally ブロックでリソースが正しく解放されることも保証されます。全体として、try-catch ブロックは、C# で例外を処理するための重要なツールの 1 つです。
1.3 throw ステートメント
C# では、手動で例外をスローするために throw ステートメントが使用されます。プログラムが throw ステートメントまで実行されると、現在のコード ブロックの実行が直ちに終了され、指定された例外オブジェクトがコール スタック内の上位 try-catch ブロックにスローされます。一致する try-catch ブロックがない場合は、プログラムのクラッシュ。
throw ステートメントの基本的な構文は次のとおりです。
throw exception;
このうち、Exception は System.Exception クラスから派生した例外オブジェクトです。throw ステートメントを使用すると、例外をカスタマイズし、それをスローして呼び出し元に例外が発生したことを通知できます。
たとえば、メソッド内のパラメータの正当性をチェックし、パラメータが要件を満たしていない場合は ArgumentException をスローできます。
public void Calculate(int value)
{
if (value <= 0)
{
throw new ArgumentException("value must be greater than zero.", nameof(value));
}
// 其他计算逻辑
}
Calculate メソッドを呼び出すときに、渡された値がゼロ以下の場合は ArgumentException がスローされ、例外メッセージに「値はゼロより大きい必要があります。」とパラメータ名「値」が表示されます。指定されます。
throw ステートメントを使用すると、例外の種類をカスタマイズし、必要に応じて例外をスローできるため、より明確で意味のある例外情報が提供されます。同時に、try-catch ブロックと throw ステートメントを適切に使用することで、プログラムの安定性と保守性を確保する例外処理メカニズムを実現できます。
1.4 最後にブロックする
C# では、finally
ブロックは、try-catch
例外が発生するかどうかに関係なく実行されるコードを含む構造体のオプションの部分です。try
ブロック内で例外がスローされたかどうかに関係なく、finally
ブロック内のコードが実行されて、リソースの正しい解放とクリーンアップが保証されます。
finally
ブロックの基本的な構文は次のとおりです。
try
{
// 可能会抛出异常的代码
}
catch (ExceptionType1 ex1)
{
// 处理 ExceptionType1 类型的异常
}
catch (ExceptionType2 ex2)
{
// 处理 ExceptionType2 类型的异常
}
// 可以有多个 catch 块,用于处理不同类型的异常
catch (Exception ex)
{
// 处理其他类型的异常
}
finally
{
// 无论是否发生异常,都会执行其中的代码
}
finally
ブロックは通常、リソースのクリーンアップ、ファイルのクローズ、データベース接続、メモリの解放などの操作を実行するために使用されます。try
ブロック内のコードは、ブロック内で例外がスローされたかどうかに関係なくfinally
実行されます。try
これは、ブロック内で例外が発生し、対応するブロックにジャンプした場合でもcatch
、finally
リソースが正しく解放されるようにブロック内のコードが実行されることを意味します。
たとえば、ファイルの読み取りおよび書き込みを使用するときに、ファイルが存在しない、またはアクセスできないなどの例外が発生した場合、finally
ファイル ストリームがブロック内で適切に閉じられていることを確認できます。
FileStream fileStream = null;
try
{
fileStream = new FileStream("example.txt", FileMode.Open);
// 其他文件读写操作
}
catch (IOException ex)
{
// 处理文件读写异常
}
finally
{
// 确保文件流被关闭
if (fileStream != null)
{
fileStream.Close();
}
}
この例では、ブロック内のコードはtry
、ブロック内で例外がスローされたかどうかに関係なくfinally
ファイル ストリームを閉じ、ファイル リソースが確実に解放されます。
finally
ブロックは、例外が発生したかどうかに関係なくコードが実行され、必要なクリーンアップが実行されることを保証するための非常に便利な構造です。
1.5 try-catch-finally ネスト
C# では、try-catch-finally
ブロックを入れ子にすることができます。つまり、try
ブロックまたはcatch
ブロックを別のブロック内に入れ子にすることができますtry-catch-finally
。finally
このような入れ子構造により、さまざまなレベルの例外を処理でき、最終的なリソースの解放とクリーンアップは最も外側のブロックで実行されます。ブロックのネストの例を
次に示します。try-catch-finally
try
{
// 外层 try 块
try
{
// 内层 try 块
// 可能会抛出异常的代码
}
catch (ExceptionType1 ex1)
{
// 处理内层 try 块中抛出的 ExceptionType1 类型的异常
}
finally
{
// 内层 finally 块,用于内层资源的释放和清理
}
}
catch (ExceptionType2 ex2)
{
// 处理外层 try 块中抛出的 ExceptionType2 类型的异常
}
finally
{
// 外层 finally 块,用于外层资源的释放和清理
}
このネストされた例では、try
内部ブロックで例外がスローされる可能性があり、例外が発生すると、対応するcatch
ブロックにジャンプして処理し、finally
内部ブロック内のコードを実行します。次に、制御は外側のtry-catch-finally
ブロックに戻り、それが実行されcatch
(一致する例外タイプがある場合)、最後に外側のブロックが実行されますfinally
。
ネストされたtry-catch-finally
ブロックを使用すると、さまざまなレベルで例外を処理し、どのような場合でもリソースが適切に解放されるようにして、コードの信頼性と安定性を維持できます。実際の開発では、ネストされた例外処理構造は、コード例外とリソース管理をより適切に管理するのに役立ちます。
2. 例外をキャッチして処理する
2.1 特定のタイプの例外をキャッチする
C# では、catch
ブロックを使用して特定の種類の例外をキャッチし、異なる種類の例外を異なる方法で処理できます。以下は、特定の種類の例外をキャッチする例です。
try
{
// 可能会抛出异常的代码
int[] arr = new int[5];
int result = arr[10]; // 会抛出 IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
// 处理 IndexOutOfRangeException 类型的异常
Console.WriteLine("发生了索引超出范围的异常:" + ex.Message);
}
catch (DivideByZeroException ex)
{
// 处理 DivideByZeroException 类型的异常
Console.WriteLine("发生了除以零的异常:" + ex.Message);
}
catch (Exception ex)
{
// 处理其他类型的异常(如果上面的 catch 块没有匹配到异常)
Console.WriteLine("发生了其他类型的异常:" + ex.Message);
}
finally
{
// 最终的资源释放和清理
}
上の例では、まず例外をスローする可能性のあるコードの実行が試行され、例外が発生すると、システムはcatch
ブロック内でスローされた例外のタイプに一致する処理ロジックを探します。一致するcatch
ブロックが見つかった場合は、そのブロック内のコードが実行され、続いてfinally
そのブロックが実行されます。一致するブロックが見つからない場合は、適切なブロックが見つかるか、メイン プログラムの最も外側のレベルに到達するcatch
まで、呼び出しスタックの検索が続けられます。特定のタイプの例外をキャッチする場合は、最も具体的な例外のタイプを最初のブロックに配置し、最も一般的なタイプを最後に配置することをお勧めします。これにより、例外処理の優先順位が正しく設定され、不必要なエラー処理が回避されます。同時に、ハンドルされない例外をtype のブロックに渡すことで、予期しない例外が発生したときにプログラムが終了しないようにすることができ、コードの安定性と信頼性が確保されます。catch
catch
Exception
Exception
catch
2.2 複数の catch ブロック
C# では、複数のcatch
ブロックを使用してさまざまな種類の例外をキャッチし、さまざまな種類の例外を異なる方法で処理できます。複数のcatch
ブロックの構文は、1 つのブロックtry
の後に複数のcatch
ブロックが続きます。各catch
ブロックは異なる例外タイプを指定しており、例外がスローされると、システムはcatch
ブロックの順序で一致する例外タイプを検索し、catch
最初に一致したブロックのコードを実行します。以下は、複数のブロックの使用
を示す例です。catch
try
{
// 可能会抛出异常的代码
int[] arr = new int[5];
int result = arr[10]; // 会抛出 IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
// 处理 IndexOutOfRangeException 类型的异常
Console.WriteLine("发生了索引超出范围的异常:" + ex.Message);
}
catch (DivideByZeroException ex)
{
// 处理 DivideByZeroException 类型的异常
Console.WriteLine("发生了除以零的异常:" + ex.Message);
}
catch (Exception ex)
{
// 处理其他类型的异常(如果上面的 catch 块没有匹配到异常)
Console.WriteLine("发生了其他类型的异常:" + ex.Message);
}
finally
{
// 最终的资源释放和清理
}
上記の例では、例外をスローする可能性のあるコードが最初に実行され、例外が発生すると、システムはcatch
ブロックの順序で一致する例外タイプを探します。一致するcatch
ブロックが見つかった場合、そのブロック内のコードが実行され、他のcatch
ブロックはスキップされます。一致するブロックが見つからない場合は、適切なブロックが見つかるか、メイン プログラムの最も外側のレベルに到達するcatch
まで、呼び出しスタックの検索が続けられます。複数のブロックを使用すると、さまざまな種類の例外をより詳細に処理できるため、コードの柔軟性と可読性が向上します。例外処理の優先順位が正しいことを確認し、不必要なエラー処理を回避するために、最も具体的な例外タイプを最初のブロックに配置し、最も一般的なタイプを最後に配置することをお勧めします。同時に、複数のブロックを使用すると、例外処理コードをより適切に編成および管理できるため、コード構造がより明確になり、保守が容易になります。catch
catch
catch
Exception
catch
2.3 基本的な例外タイプをキャッチする
C# では、多くの基本的な例外タイプをキャッチできます。一般的な基本的な例外の種類とその使用法をいくつか示します。
System.Exception
: これは、すべての例外タイプの基本クラスです。通常、この例外は直接キャッチしませんが、そのサブクラスを使用して特定の種類の例外をキャッチします。System.ArithmeticException
: ゼロ除算などの算術演算における例外を示します。System.IndexOutOfRangeException
: 配列インデックスが範囲外の例外を示します。System.NullReferenceException
: null 参照オブジェクトのメンバーにアクセスしようとしたときにスローされる、null 参照例外を示します。System.OutOfMemoryException
: 必要なメモリを割り当てられない場合にスローされる、メモリ不足例外を示します。System.DivideByZeroException
: 除算またはモジュロ演算の分母がゼロの場合にスローされるゼロ除算例外を示します。System.StackOverflowException
: スタック オーバーフロー例外を示します。通常、再帰呼び出し中に発生します。System.IO.IOException
: ファイルおよびストリームの読み取りおよび書き込み操作のエラーを処理するために使用される入力例外と出力例外を示します。System.FormatException
: 書式設定例外を示します。通常、文字列が別の型に変換されるときに発生します。System.ArgumentException
: パラメータ例外を示します。通常、無効なパラメータ値が渡されたときにスローされます。System.NotSupportedException
: サポートされていないメソッドまたは関数が呼び出されたときにスローされる、サポートされていない操作例外を示します。
上記の基本的な例外の種類に加えて、C# ではキャッチできる例外の種類が他にも多数あります。コードを記述するときは、例外を適切に処理してエラーから回復できるように、特定の状況に応じてキャッチする適切な例外の種類を選択する必要があります。同時に、特定のアプリケーション ロジック エラーを表す例外タイプをカスタマイズして、コードの可読性と保守性を向上させることもできます。
2.4 キャッチされなかった例外の結果
例外の種類と例外が発生する場所によっては、例外がキャッチされないとプログラムが予期せず終了したり、不安定になったりする可能性があります。例外がキャッチされなかった場合に考えられる影響をいくつか示します。
- プログラムのクラッシュ: キャッチされなかった例外によりプログラムがクラッシュし、コンソールまたはログにエラー メッセージが表示されて実行が終了する可能性があります。これにより、ユーザー エクスペリエンスが低下し、データ損失やファイル破損が発生する可能性もあります。
- データの損失: 例外が発生したときに、その例外が正しく処理されないと、保存されていないデータが失われる可能性があります。たとえば、ファイルの読み取りおよび書き込み操作で例外が発生したが、それが正しく処理されなかった場合、書き込まれたファイルの内容は不完全であるか破損している可能性があります。
- メモリ リーク: 一部の例外により、リソースが正しく解放されず、メモリ リークが発生する場合があります。メモリ リークが繰り返し発生すると、最終的にはプログラムの実行が遅くなったり、クラッシュしたりする可能性があります。
- 不安定性: キャッチされない例外はプログラムの不安定性につながる可能性があり、予測と保守が困難になります。未処理の例外はプログラムのさまざまな部分で再発する可能性があるため、追跡して修正することが困難になります。
- セキュリティの問題: 未処理の例外はハッカーによって悪用され、セキュリティ ホールを引き起こす可能性があります。ハッカーは例外を使用して機密情報を取得したり、不正な操作を実行したりする可能性があります。
キャッチされない例外による影響を回避するには、開発者はプログラム内で例外処理メカニズムを適切に使用する必要があります。例外をキャッチして処理することで、プログラムのフローをより適切に制御し、エラー状態を処理するための適切なアクションを実行できます。同時に、問題発生時の調査やトラブルシューティングのために、ログ システムを使用して異常情報を記録することをお勧めします。例外を適切に処理すると、プログラムの安定性と信頼性が向上します。
3. カスタム例外
3.1 カスタム例外クラスの作成
C# では、カスタム例外クラスの作成は非常に簡単です。Exception クラスを拡張することで、独自の例外クラスを定義できます。以下は、カスタム例外クラスの作成方法を示すサンプル コードです。
using System;
// 自定义异常类
public class MyCustomException : Exception
{
// 构造函数
public MyCustomException()
{
}
// 带有异常消息的构造函数
public MyCustomException(string message)
: base(message)
{
}
// 带有异常消息和内部异常的构造函数
public MyCustomException(string message, Exception innerException)
: base(message, innerException)
{
}
}
// 使用自定义异常类的示例
public class Program
{
public static void Main()
{
try
{
// 抛出自定义异常
throw new MyCustomException("这是一个自定义异常");
}
catch (MyCustomException ex)
{
// 捕获并处理自定义异常
Console.WriteLine("捕获到自定义异常:" + ex.Message);
}
catch (Exception ex)
{
// 捕获其他异常
Console.WriteLine("捕获到异常:" + ex.Message);
}
}
}
上記のコードでは、MyCustomException
C# の Exception クラスを継承する という名前のカスタム例外クラスを定義しました。throw
次に、キーワードを使用してMain メソッドでカスタム例外をスローし、catch
ブロックでこのカスタム例外をキャッチして処理します。
例外クラスをカスタマイズすることにより、ビジネス ニーズと例外の種類に基づいて、より意味のある例外を作成し、プログラム内で発生するエラー状態をより適切に処理および識別できるようになります。これにより、コードがより明確になり、保守が容易になり、プログラムの読みやすさと信頼性が向上します。
3.2 カスタム例外をスローする
C# では、カスタム例外クラスを作成することでカスタム例外をスローできます。まず、Exception クラスから継承したカスタム例外クラスを定義し、キーワードを使用してthrow
カスタム例外をスローする必要があります。以下は、カスタム例外をスローする方法を示すサンプル コードです。
using System;
// 自定义异常类
public class MyCustomException : Exception
{
// 构造函数
public MyCustomException()
{
}
// 带有异常消息的构造函数
public MyCustomException(string message)
: base(message)
{
}
// 带有异常消息和内部异常的构造函数
public MyCustomException(string message, Exception innerException)
: base(message, innerException)
{
}
}
// 使用自定义异常类的示例
public class Program
{
public static void Main()
{
try
{
// 模拟一个条件,如果满足,则抛出自定义异常
bool condition = true;
if (condition)
{
// 抛出自定义异常
throw new MyCustomException("这是一个自定义异常");
}
else
{
Console.WriteLine("条件不满足,没有抛出异常。");
}
}
catch (MyCustomException ex)
{
// 捕获并处理自定义异常
Console.WriteLine("捕获到自定义异常:" + ex.Message);
}
catch (Exception ex)
{
// 捕获其他异常
Console.WriteLine("捕获到异常:" + ex.Message);
}
}
}
上記のコードでは、MyCustomException
C# の Exception クラスを継承する という名前のカスタム例外クラスを定義しました。次に、キーワードを使用して Main メソッドでthrow
カスタム例外をスローします。
実際のアプリケーションでは、特定の条件が満たされたときに、throw
キーワードを介してカスタム例外をスローし、プログラム内で積極的に例外を発生させ、適切な例外処理を行うことができます。これにより、コードの柔軟性と信頼性が向上し、デバッグやトラブルシューティングに便利なより多くの例外情報も提供できるようになります。
3.3 カスタム例外をキャプチャして処理する
C# では、カスタム例外のキャッチと処理は、組み込み例外のキャッチと非常に似ています。コード内でthrow
カスタム例外をスローするために を使用すると、try-catch
ブロックを通じてこれらのカスタム例外をキャッチして処理できます。以下は、カスタム例外をキャッチして処理する方法を示すサンプル コードです。
using System;
// 自定义异常类
public class MyCustomException : Exception
{
// 构造函数
public MyCustomException()
{
}
// 带有异常消息的构造函数
public MyCustomException(string message)
: base(message)
{
}
// 带有异常消息和内部异常的构造函数
public MyCustomException(string message, Exception innerException)
: base(message, innerException)
{
}
}
// 使用自定义异常类的示例
public class Program
{
public static void Main()
{
try
{
// 模拟一个条件,如果满足,则抛出自定义异常
bool condition = true;
if (condition)
{
// 抛出自定义异常
throw new MyCustomException("这是一个自定义异常");
}
else
{
Console.WriteLine("条件不满足,没有抛出异常。");
}
}
catch (MyCustomException ex)
{
// 捕获并处理自定义异常
Console.WriteLine("捕获到自定义异常:" + ex.Message);
}
catch (Exception ex)
{
// 捕获其他异常
Console.WriteLine("捕获到异常:" + ex.Message);
}
}
}
上記のコードでは、MyCustomException
というカスタム例外クラスを定義し、throw
カスタム例外をスローするために使用します。Main
このメソッドでは、try-catch
ブロックを使用して、スローされる可能性のある例外をキャッチします。条件が満たされると、カスタム例外が捕捉されて処理され、例外情報が出力されます。条件が満たされない場合は、例外はスローされず、対応するプロンプト情報が直接出力されます。
4. 例外チェーン
4.1 InnerException プロパティ
C# では、プロパティは、現在の例外の原因となった内部例外 (つまり、入れ子になった例外) を取得または設定するために使用されるクラスのメンバーですInnerException
。Exception
例外が別の例外によってトリガーされた場合、InnerException
属性を使用して外側の例外の詳細を取得できます。これは、デバッグやトラブルシューティングに非常に役立ちます。
InnerException
プロパティの型は です。これは、クラスから継承する任意System.Exception
の例外オブジェクトを含めることができることを意味します。Exception
現在の例外が他の例外によって発生した場合、InnerException
プロパティにはこの外部例外オブジェクトが含まれます。現在の例外がルート例外である場合 (つまり、他の例外が発生していない場合)、プロパティは になりInnerException
ますnull
。C# で属性を使用する方法を示すサンプル コードを次に示しますInnerException
。
using System;
public class Program
{
public static void Main()
{
try
{
// 在外部方法中抛出异常
OuterMethod();
}
catch (Exception ex)
{
// 捕获异常,并获取内部异常
if (ex.InnerException != null)
{
Console.WriteLine("捕获到外部异常:" + ex.Message);
Console.WriteLine("外部异常的类型:" + ex.GetType().FullName);
Console.WriteLine("内部异常的消息:" + ex.InnerException.Message);
Console.WriteLine("内部异常的类型:" + ex.InnerException.GetType().FullName);
}
else
{
Console.WriteLine("捕获到根异常:" + ex.Message);
Console.WriteLine("根异常的类型:" + ex.GetType().FullName);
}
}
}
public static void OuterMethod()
{
try
{
// 在内部方法中抛出异常
InnerMethod();
}
catch (Exception ex)
{
// 将内部异常包装并抛出外部异常
throw new Exception("这是一个外部异常", ex);
}
}
public static void InnerMethod()
{
// 抛出内部异常
throw new Exception("这是一个内部异常");
}
}
上記のコードでは、InnerMethod()
、 、OuterMethod()
の3 つのメソッドを定義しましたMain()
。InnerMethod()
内部例外がメソッドでスローされ、その後、その例外がメソッドでキャッチされ、OuterMethod()
外部例外にラップされてスローされます。このメソッドではMain()
、外部例外をキャッチし、InnerException
属性を使用して内部例外の情報を取得します。出力には、外部例外のメッセージとタイプ、および内部例外のメッセージとタイプが表示されます。
4.2 例外チェーンの構築
C# では、InnerException
属性を使用して例外チェーンを構築し、例外を別の例外にネストして例外チェーンを形成できます。これは、複数の例外階層を処理する場合、または外部例外をキャッチしながら内部例外をラップする場合に便利です。
以下は、例外チェーンを構築する方法を示すサンプル コードです。
using System;
public class Program
{
public static void Main()
{
try
{
// 在外部方法中抛出异常
OuterMethod();
}
catch (Exception ex)
{
// 捕获异常,并获取内部异常链
PrintExceptionChain(ex);
}
}
public static void OuterMethod()
{
try
{
// 在内部方法中抛出异常
InnerMethod();
}
catch (Exception ex)
{
// 将内部异常包装并抛出外部异常
throw new Exception("这是一个外部异常", ex);
}
}
public static void InnerMethod()
{
// 抛出内部异常
throw new Exception("这是一个内部异常");
}
public static void PrintExceptionChain(Exception ex)
{
// 打印异常链
Console.WriteLine("异常链:");
while (ex != null)
{
Console.WriteLine("异常消息:" + ex.Message);
Console.WriteLine("异常类型:" + ex.GetType().FullName);
ex = ex.InnerException;
}
}
}
上記のコードでは、InnerMethod()
、OuterMethod()
、Main()
、およびヘルパー メソッド の3 つのメソッドを定義していますPrintExceptionChain()
。InnerMethod()
内部例外がメソッドでスローされ、その後、その例外がメソッドでキャッチされ、OuterMethod()
外部例外にラップされてスローされます。このメソッドではMain()
、この外部例外をキャッチし、PrintExceptionChain()
メソッドを使用して例外チェーンを出力します。コードを実行すると、例外チェーンに内部例外と外部例外の詳細が含まれていることがわかります。
5. ベストプラクティスと注意事項
例外処理を使用する場合のベスト プラクティスと考慮事項をいくつか示します。
- 例外処理は例外的な状況にのみ使用する: 例外処理は、予期しないエラー状態を処理するために使用する必要があり、プログラムの通常のフローを制御するために使用するべきではありません。例外処理を過度に使用するとパフォーマンスに影響を与える可能性があるため、通常のフロー中に例外をスローしたりキャッチしたりすることはできる限り避ける必要があります。
- 特定の例外タイプを使用する: ジェネリック タイプを使用する代わりに、特定の例外タイプを使用して特定のエラーを捕捉するようにしてください
Exception
。これにより、さまざまなタイプの例外をより正確に識別して処理できるようになり、コードの可読性と保守性が向上します。 - 例外を処理することは合理的である必要があります。例外をキャッチした後、ロギング、ユーザーへのエラー メッセージの表示、トランザクションのロールバックなど、例外を処理するための適切な措置を講じる必要があります。単に例外を無視したり、何も処理しなかったりすると、デバッグが困難な問題が発生する可能性があります。
- 空の catch ブロックを避ける: 空のブロックの使用は避けてください。空の
catch
ブロックを使用すると、例外が無視され、問題の特定と修正が困難になります。適切な処理ロジックがない場合は、例外を上位層にスローし続けるか、少なくともログを記録することを検討できます。 - ループ内で例外をキャッチしないようにする: ループ内で例外をキャッチすると、パフォーマンスの問題が発生する可能性があります。可能であれば、ループの外で例外を処理するか、ループ内で条件文を使用して例外を回避します。
- リソースを解放するには、finally ブロックを使用します。
try
ブロック内でリソース (ファイル、データベース接続など) を開く場合は、finally
リソースがブロック内で時間内に解放されることを確認する必要があり、次の場合でも解放操作を実行できます。例外が発生します。 - 適切なタイミングで例外をキャッチする: 外部リソース (ファイルの読み書き、ネットワーク リクエストなど) や例外を引き起こす可能性のある操作にアクセスするときの例外処理など、例外は適切なタイミングでキャッチして処理する必要があります。
- カスタム例外クラスを使用する: 場合によっては、特定のエラー状態を表現し、例外の読みやすさと保守性を向上させるためにカスタム例外クラスを定義することが必要になることがあります。
- 例外処理コードを定期的に確認する: 例外処理コードはコードが変更されると変更される可能性があるため、例外処理コードがまだ有効であることを確認するために、定期的にチェックして保守する必要があります。
- 適切なレベルで例外を処理する: 例外は適切なレベルで処理される必要があります。ビジネス関連の例外はビジネス ロジック層で処理され、システム エラーや未処理の例外などのより一般的な例外は上位層で処理されます。
6. まとめ
この記事では、C#における例外処理の重要性と仕組みについて詳しく紹介します。例外とは、プログラムの実行中に発生するエラーまたは異常な状況であり、プログラムの堅牢性と安定性に重要な役割を果たします。
この記事では、まず例外の概念と例外クラスの継承構造について説明し、さまざまな種類の例外をより適切に捕捉して処理できるように、Exception クラスを継承してカスタム例外クラスを作成します。次に、try-catch ブロックの使用法を紹介します。これにより、例外をキャッチして catch ブロックで処理することで、プログラムの実行を継続したり、適切な措置を講じたりすることができます。
この記事では、例外処理のベスト プラクティスに関して、適切なエラー メッセージ出力や、リソースを解放するための Final ブロックの使用などの考慮事項を強調しています。さらに、例外チェーンを構築し、InnerException プロパティを使用すると、例外の追跡と処理が向上します。