Cでカスタム例外を正しく作成する方法#

記事の対象者



この記事は主に.NETの初心者を対象としていますが、C#を使用してユーザー定義の例外を適切に作成する方法を完全に理解していない経験のある開発者にとっても役立ちます。



この記事のサンプルコードはこちらからダウンロードできます



簡単な例外を作成する



プログラマーによって作成されたコードで発生した例外を、標準の.NET Frameworkタイプで発生する例外から明確に分離する必要がある場合、C#で独自の例外タイプを作成することをお勧めします。



たとえば、ユーザー名を変更するように設計されたメソッドがあります。



private static void EditUser(string oldUserName, string newUserName) { var userForEdit = GetUserByName(oldUserName); if (userForEdit == null) return; else userForEdit.Name = newUserName; }
      
      







このメソッドは、それに割り当てられたタスクを解決しますが、1つの重要なことは行いません-指定された名前のユーザーが不在であると報告せず、名前を変更する操作が実行されていません。



例外的な状況について通知するために、標準的な例外を生成できますが、これは推奨される方法ではありません。



 private static void EditUser(string oldUserNane, string newUserName) { var userForEdit = GetUserByName(oldUserName); if(userForEdit == null) throw new Exception(); else userForEdit.Name = newUserName; }
      
      







特定のアプリケーションのレベルで例外が生成されたことを簡単に判断できるようにするには、独自のカスタム例外を作成する必要があります。nullになったら、適切なユーザーの代わりにスローします。



独自の例外を作成することは難しくありません。System.ExceptionまたはSystem.ApplicationExceptionを継承するパブリッククラスを定義する必要があります。 これは良い方法ではありませんが、生成された例外クラス内のコードはまったく省略できます。



 public class UserNotFoundException : ApplicationException { }
      
      







System.ExceptionまたはSystem.ApplicationExceptionから継承する最良の方法は何ですか?
これらの各タイプは、特定の目的を対象としています。 System.Exceptionはすべてのユーザー定義例外の共通クラスですが、 System.ApplicationExceptionはアプリケーション固有のレベルで発生する例外を定義します。



たとえば、この記事のテストアプリケーションは別のプログラムであるため、System.ApplicationExceptionから定義した例外を継承することは完全に受け入れられます。





ここで、Exceptionの代わりに、作成したUserNotFoundExceptionを発生させます。



 private static void EditUser(string oldUserNane, string newUserName) { var userForEdit = GetUserByName(oldUserName); if(userForEdit == null) throw new UserNotFoundException(); else userForEdit.Name = newUserName; }
      
      







この場合、例外に関するメッセージは「アプリケーションのエラー」になります 。 これはあまり有益ではありません。



カスタム例外クラスコードが.NETガイドラインに準拠するには、次のルールに従う必要があります





個々のコンストラクターの目的について少し説明します。「内部例外」を処理するためのコンストラクターは、この例外を引き起こした例外をそれに渡すために必要です。 詳細については、以下のネタバレ「追加フィールドの追加、それらのシリアライゼーションおよびデシリアライゼーション」で型のシリアライゼーションをサポートするコンストラクタが必要なのはなぜですか



プログラマがVisual Studioで同じコードを記述する必要をなくすために、上記のすべての推奨事項を満たす例外クラスを生成する例外スニペットがあります







したがって、推奨事項を実装した後、例外のコードは次のようになります。



 public class UserNotFoundException : ApplicationException { public UserNotFoundException() { } public UserNotFoundException(string message) : base(message) { } public UserNotFoundException(string message, Exception inner) : base(message, inner) { } protected UserNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } }
      
      







これで、例外をスローするときに、発生の理由をより詳細に示すことができます。



 throw new UserNotFoundException("User \"" + oldUserName + "\" not found in system");
      
      







追加のフィールドを追加し、それらをシリアライズおよびデシリアライズします
検索したいユーザーの名前を格納する例外のクラスに追加フィールドを追加したいが、最終的には見つからなかったと仮定します(そのため実際に例外が生成されたため)。 追加の文字列フィールドを例外クラスに追加します。



 [Serializable] public class UserNotFoundException : ApplicationException { private string _userNotFoundName; public string UserNotFoundName { get { return _userNotFoundName; } set { _userNotFoundName = value; } } public UserNotFoundException() { } public UserNotFoundException(string message) : base(message) { } public UserNotFoundException(string message, Exception inner) : base(message, inner) { } protected UserNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } }
      
      







問題は、追加したフィールドのデータが自動的にシリアル化および逆シリアル化されないことです。 CLRが例外のデータを正しくシリアル化および逆シリアル化することを確認する必要があります。



フィールドをシリアル化するには、 ISerializableインターフェイスで記述されたGetObjectDataメソッドをオーバーライドする必要があります。 GetObjectDataメソッドは、 SerializationInfoオブジェクトにシリアル化用のデータを入力します。 SerializationInfoで、フィールドの名前とそこに格納されている情報を渡す必要があります。



 public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("UserNotFoundName", this.UserNotFoundName); }
      
      







デフォルトで例外のすべてのフィールド(Message、TargetSite、HelpLinkなど)をSerializationInfoに追加するには、基本クラスのGetObjectDataメソッドを呼び出す必要があります。



逆シリアル化も同様の流れで行われます。 逆シリアル化中にSerializationInfoStreamingContextを受け入れて、例外のコンストラクターが呼び出されます。 私たちのタスクは、 SerializationInfoからデータを取得し、作成したフィールドに書き込むことです。



 protected UserNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { if (info != null) { this._userNotFoundName = info.GetString("UserNotFoundName"); } }
      
      









そして最後のタッチ-特定のタイプの例外をスローできる情報のメソッドのXMLドキュメント(もちろん、使用する場合)への追加:



 /// <exception cref="UserDefinedException.UserNotFoundException" />
      
      







これで、ユーザー定義の例外を使用する準備が整いました。 あなたの心が望むものにそれを追加することができます:例外の状態を記述する追加フ​​ィールド、追加情報を含むなど。



PS:例外クラスの追加フィールドをシリアライズおよびデシリアライズする方法に関する情報を追加しました。 ネタバレ「追加フィールドの追加、それらのシリアライゼーションおよびデシリアライゼーション」の詳細。



PPS:コメントと健全な批判をありがとう。 記事を最後まで読んだ人-コメントも読んで、有用な情報があります。



All Articles