C#で例外を安全に処理する

構造的例外は、エラー(例外的なものを含む)の状況を処理するための重要なメカニズムの1つです。 以下にリストするのは、C#および、より一般的には.NETプラットフォームで例外を処理する場合の全体的なコード品質を向上させるプログラミングガイドラインです。

自分のクラス 。 例外に直接基づいてではなく、 Exceptionから継承した独自のクラスに基づいて例外をスローします。これにより、独自のハンドラーを定義し、コードと.NETフレームワークのコードによってスローされた例外の追跡と処理を分離できるためです。

個別のフィールドMessageフィールドのデータをシリアライズおよびデシリアライズする代わりに、独自のクラスに個別のフィールドを作成して、重要な情報を伝えます。 複雑なMessageデータをMessageのJSON文字列としてパックするという考えは魅力的ですが、エンコード、ローカライズ、およびデコードのためのリソースの追加コストが追加されるため、これはめったに良いアイデアではありません。

ログへのメッセージ 。 ハンドラーがException.ToString();呼び出してException.ToString();キャッチするたびにメッセージを記録しますException.ToString(); これにより、例外のデバッグ時に追跡が容易になります。

正確なクラス 。 最も一般的でないクラスを使用して例外をキャッチします。そうしないと、検出が困難なエラーが発生する可能性があります。 次のコードを検討してください。

 public class SimpleClass { public static string DoSomething() { try { return SimpleLibrary.ReportStatus(); } catch (Exception) { return "Failure 1"; } } } public class SimpleLibrary { public static string ReportStatus() { return String.Format("Success {0}.", 0); } } 


SimpleClass SimpleLibrarySimpleLibraryのコードが別々のアセンブリにあると仮定した場合、両方のアセンブリが正しくインストールされた場合、コードは正しく実行され、メッセージ「Success 0」が表示され、 SimpleLibraryクラスのアセンブリがインストールされなかった場合、コードは正しく実行されません。 ReportStatus関数の実行中にエラーが発生しなかったにもかかわらず、「Failure 1」というメッセージが表示されます。 過度に一般化された例外処理のため、問題は明らかではありません。 行をフォーマットするコードはArgumentNullExceptionおよびFormatExceptionスローFormatExceptionため、catchブロックでキャッチする必要があるのはこれらの例外です。エラーの原因は明白になります。これはSimpleLibraryクラスを含むアセンブリの不在または誤ったインストールによるFileNotFoundException例外です。

実質的な処理 。 常に例外を情報に基づいて処理します。 コードを表示

 try { DoSomething(); } catch (SomeException) { // TODO: ... } 


問題を隠し、デバッグまたは実行中に検出されないようにします。

finallyブロックでクリアしますfinallyブロックの一時オブジェクトを削除します。 一時ファイルを書き込む操作を検討してください。

 void WorkWithFile(string filename) { try { using (StreamWriter sw = new StreamWriter(filename)) { // TODO: Do something with temporary file } File.Delete(filename); } catch (Exception) { File.Delete(filename); throw; } } 


ご覧のとおり、ファイルを削除するコードが重複しています。 繰り返しを避けるために、 finallyブロックから一時ファイルを削除する必要があります。

 void WorkWithFile(string filename) { try { using (StreamWriter sw = new StreamWriter(filename)) { // TODO: Do something with temporary file } } finally { File.Delete(filename); } } 


ステートメントを使用する usingステートメントを使用します。 オブジェクトのメソッドを呼び出すときに例外が発生した場合でも、 Disposeメソッドの呼び出しを保証します。

 using (Font f = new Font("Times New Roman", 12.0f)) { byte charset = f.GdiCharSet; } 


usingステートメントの使用は、 try/finallyブロックtry/finally同等ですが、よりコンパクトで簡潔です。

 Font f = new Font("Times New Roman", 12.0f); try { byte charset = f.GdiCharSet; } finally { if (f != null) ((IDisposable)f).Dispose(); } 


関数の結果 。 関数の結果を返すために例外を使用したり、エラーを処理するために特別なリターンコードを使用したりしないでください。 それぞれに-彼自身。 関数の結果を返す必要があり、スキップできないエラーは例外で処理する必要があります。

リソース不足 。 リソースがない場合はnull返しnull 。 .NET APIで一般的に受け入れられている規則によれば、関数はリソースがない場合に例外をスローしてはならず、 nullを返す必要がありnull 。 そのGetManifestResourceStream 、リソースがコンパイル時に指定されなかった場合、または呼び出し元のコードから見えない場合、 GetManifestResourceStreamnull返しnull

出発地 。 例外が発生した場所を追跡します。 例えば

 try { // Do something to throw exception } catch (Exception e) { // Do something to handle exception throw e; // Wrong way! throw; // Right way } 


コールスローの場合、 throw e; 新しい例外を作成するとコールスタックがクリアされるため、例外がスローされる場所に関する情報は新しい行に置き換えられます。 代わりに、 throw;呼び出しを行う必要がありますthrow; 単に元の例外をスローします。

意味を追加します。 元の例外を変更して、ユーザーが必要とする意味を追加します。 たとえば、データベースに接続する場合、ユーザーは接続障害の原因(ソケットエラー)、障害自体に関する十分な情報に無関心かもしれません。

連載 [Serializable]で例外を継承可能Exception例外に継承可能にしException 。 これは、Webサービスなどで例外が受信される場所が事前にわからないため、便利です。

 [Serializable] public class SampleSerializableException : Exception { public SampleSerializableException() { } public SampleSerializableException(string message) : base(message) { } public SampleSerializableException(string message, Exception innerException) : base(message, innerException) { } protected SampleSerializableException(SerializationInfo info, StreamingContext context) : base(info, context) { } } 


標準コンストラクタ 。 標準のコンストラクタを実装します。そうしないと、正しい例外処理が困難になる場合があります。 したがって、 NewExceptionを除外するNewExceptionこれらのコンストラクターは次のとおりです。




MSDN、CodeProject、StackOverflowの資料に基づいています。

Source: https://habr.com/ru/post/J178805/


All Articles