[C#]例外処理のメモ

例外処理をどう扱うべきかイマイチ分からないので、テキトーに調べたメモ

参考サイト

SqlException のハンドリング

ハードウェアのトラブルのような致命的なエラーも含まれるので どのようなエラーか判定して例外ハンドリングする必要がある。 エラー内容は SqlException が持っている SqlError の中身から判断する。

SqlError クラス

SqlError クラス

SqlException が持っているSQLエラー情報を持つクラス。以下はコピペ + 適当に書き込んだ

#region アセンブリ System.Data.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll
#endregion

using System;

namespace System.Data.SqlClient
{
  // 概要:
  //   SQL Server から返された警告またはエラーに関する情報を収集します。
  [Serializable]
  public sealed class SqlError
  {
    // 概要:
    //   SQL Server から返されたエラーの重大度レベルを取得します。
    //
    // 戻り値:
    //   エラーの重大度レベルを示す 1 ~ 25 の値。 既定値は 0 です。
    //   重要度10以下 | 情報メッセージ - ユーザが入力した情報が誤っている
    //   重要度11~16 | ユーザーが生成し、修正可能
    //   重要度17~19 | ソフトorハードのエラー
    //   重要度20~25 | ソフトorハードのエラー。SqlConnectionを閉じる
    //   ※詳細 : https://msdn.microsoft.com/ja-jp/library/ms164086.aspx
    public byte Class { get; }

    //
    // 概要:
    //   Transact-SQL コマンドのバッチまたはストアド プロシージャ内の、エラーが含まれる行の番号を示します。
    //
    // 戻り値:
    //   Transact-SQL コマンドのバッチまたはストアド プロシージャ内の、エラーが含まれる行の番号。
    public int LineNumber { get; }

    //
    // 概要:
    //   エラーを説明するテキストを取得します。
    //
    // 戻り値:
    //   エラーを説明するテキスト。 SQL Server によって生成されるエラーの詳細については、SQL Server Books Online を参照してください。
    public string Message { get; }

    //
    // 概要:
    //   エラーの種類を示す番号を取得します。
    //
    // 戻り値:
    //   エラーの種類を示す番号。
    //   ~100 : 接続前のエラー
    //   21, 101~ : SQLサーバー内のエラー
    //   547 : 外部キー制約違反
    //   2601 : 一意制約違反
    //   2627 : PK制約違反
    //   ※参考 : https://msdn.microsoft.com/ja-jp/library/cc645611.aspx
    //   ※参考 : http://www.tomoyan.net/dokuwiki/dotnet/sqlexception?s[]=sqlerror
    //   ※参考 : http://www.codekeep.net/snippets/c3cf5c38-932d-4aff-8100-a0bf17c4a5c0.aspx
    //
    //
    public int Number { get; }

    //
    // 概要:
    //   エラーを生成したストアド プロシージャまたはリモート プロシージャ コール (RPC) の名前を取得します。
    //
    // 戻り値:
    //   ストアド プロシージャまたは RPC の名前。 SQL Server によって生成されるエラーの詳細については、SQL Server Books
    //   Online を参照してください。
    public string Procedure { get; }

    //
    // 概要:
    //   エラーを生成した SQL Server インスタンスの名前を取得します。
    //
    // 戻り値:
    //   SQL Server インスタンスの名前。
    public string Server { get; }

    //
    // 概要:
    //   エラーを生成したプロバイダーの名前を取得します。
    //
    // 戻り値:
    //   エラーを生成したプロバイダーの名前。
    public string Source { get; }

    //
    // 概要:
    //   エラー、警告、または "データがありません" というメッセージを表す、数字によるエラー コードを SQL Server から取得します。
    //
    // 戻り値:
    //   エラー コードを表す数字。
    public byte State { get; }

    // 概要:
    //   エラー メッセージの完全なテキストを取得します。
    //
    // 戻り値:
    //   エラーの完全なテキスト。
    public override string ToString();
  }
}

全部を想定するとか非現実的なので、Number で特定のエラーかどうかをチェックして、特定のエラーでなければは再スローでいいっぽい。

try
{
  // SQLの操作
}
catch (SqlException ex)
{
  switch (ex.Number)
  {
    case 547: // 外部キー制約違反
      break;

    case 2601: // 一意キー制約違反
    case 2627: // PK制約違反
      break;

    default:
      throw;
  }
}

キャッチすべき例外が多すぎる…

例外は必要なければキャッチしないので多すぎることはあまり無い。

try
{
  XmlDocument xml = new XmlDocument();
  xml.Load(xmlFilePath);
}
catch(Exception ex)
{
  if (ex is XmlException ||
    ex is SecurityException ||
    ex is PathTooLongException ||
    ex is DirectoryNotFoundException ||
    ex is FileNotFoundException ||
    ex is UnauthorizedAccessException ||
    ex is ArgumentException ||
    ex is ArgumentNullException)
  {
    // エラーハンドリング
  }
  else
  {
    throw;
  }
}

例外サブクラスの種類

例外は大きく SystemExceptionApplicationException の2つがあるけど両者に違いは無い(C#と諸々 業務エラーの表現方法 再び)

細かい説明を見るには Exception クラス (System) から派生クラスを辿っていけばOK

※以下は名前空間を省略してます
※全部のせてません

SystemException
│
├ AccessViolationException : 保護されたメモリの読み書き試行
├ StackOverflowException : これは.NET 2.0移行はキャッチ不可
├ SecurityException : セキュリティーエラーの検知
├ ExecutionEngineException : 基本的にキャッチしたらダメ
│
├ BadImageFormatException : DLL or EXEのファイルイメージが無効
├ ReflectionTypeLoadException : Module#GetTypeで読み込み失敗
│
├ AuthenticationException : 認証ストリームの認証失敗(再試行可能)
│ └ InvalidCredentialException : 認証ストリーム認証失敗(再試行不可)
│
├ UnauthorizedAccessException
│ └ PrivilegeNotHeldException
│
├ TimeoutException : タイムアウト
│
├ NotImplementedException : 未実装な時にとりあえず
│
├ NotSupportedException : 未サポートなメソッドを読んだ
│ └ PlatformNotSupportedException : プラットフォームが未サポート
│
├ ConfigurationException : 下の例外の下位互換
│ └ ConfigurationErrorsException : 構成ファイルのI/Oエラーor内容不正
│
├ NullReferenceExceptoin : ぬるぽ
├ InvalidCastException : キャスト失敗
├ IndexOutOfRangeException : 配列外インデックスにアクセス
├ KeyNotFoundException : コレクションのキーが無い
├ RankException : 次元の違う配列をメソッドに渡す
├ ArrayTypeMismatchException : 共変性違反
│
├ XmlSyntaxException : XML構文エラー
├ XmlException : 最後の例外ってなんだよ…
├ XPathException : 不正なXPath
├ XsltException : XSLT変換エラー
│ └ XsltCompileError : XSLTスタイルシートエラー
│
├ OutOfMemoryException : メモリ不足
│└  InsufficientMemoryException : 空きメモリチェックしたらメモリ不足
│
├ InvalidOperationException : 引数が原因じゃないときにスロー
│ ├ PingException : Pingクラスが投げる
│ ├ ProtocolViolationException : ネットワークプロトコルの使用中のエラー
│ ├ WebException
│ └ ObjectDisposedException : Dispose済みのオブジェクトに触れた
│
├ MemberAccessExcepton : メンバアクセスのエラー
│ ├ FieldAccessException : 不可視なフィールドにアクセス
│ ├ MethodAccessException : 不可視なメソッドにアクセス
│ └ MissingMemberException : 存在しないメンバに動的アクセス
│
├ ArgumentException : 引数例外
│ ├ ArgumentNullException : 引数にnull
│ ├ ArgumentOutOfRangeException : 引数に範囲外の値を指定
│ ├ InvalidEnumArgumentException : 引数に無効なEnumを指定
│ ├ EncoderFallbackException : 文字などのエンコードフォールバック失敗
│ ├ DecoderFallbackException : デコードフォールバック失敗
│ ├ DuplicateWaitObjectException : 同期関係
│ └ InvalidAsynchronousStateException : 非同期関係
│
├ ArithmeticException : 算術orキャストor変換のエラー
│ ├ DivideByZeroException : 浮動小数点値は0でも割れるよ
│ ├ NotFiniteNumberException : 浮動小数点値が±無限大orNan
│ └ OverflowException : 桁あふれ
│
├ IOException
│ ├ DriveNotFoundException : 使用できないドライブor共有にアクセス
│ ├ DirectoryNotFoundException
│ ├ FileNotFoundExceptoin
│ ├ PathTooLongException : 超長くするとUriExceptionが飛ぶ罠
│ ├ EndOfStreamException : ストリームの末尾オーバー
│ └ FileLoadException : アセンブリがうまく読み込めない
│
├ FormatException : Parse系関数からしばしば飛ぶ
│ ├ CookieException : 長すぎるクッキーをCookieConatinerに
│ ├ CustomAttributeFormatException
│ └ UriFormatException : 無効なURI
│
└ WarningException : エラーではなく警告
Share
関連記事