04:29まず最初に謝ります。書いたことを取り消させてください。「業務エラーは例外で表現するべき」と考えます。その理由について語っていきます。】この記事で使用する用語について、以下のように定義します。・例外System.Exceptionから派生する全ての例外クラスを指します。正常系ではないフローを指します。エラーは業務エラーとシステムエラーに大別できます。・業務エラーエンドユーザーの操作次第で発生しうるエラーを指します。業務を遂行する上で間違った操作をエンドユーザーが行った場合に発生するエラーや、エンドユーザーが操作を行っている間に、他のエンドユーザーが何らかの操作を行ったことによって、現在の操作が有効でなくなった場合に発生するエラーなどです。・システムエラーシステム異常やネットワーク異常、プログラムの欠陥など、エンドユーザーに非がないエラーを指します。業務エラーとかシステムエラーとかはひとまず置いといて、まずは「エラー」を例外で表す場合の利点を挙げてみます。例外時の処理コードを分離できるバグを検出しやすい詳細な情報を保持しているこんな所でしょうか。それぞれ見ていきましょう。]構造化例外処理は、その名が表すように、「例外処理コードを分離・隔離」することによって「構造化」します。可読性、保守性に優れるということです。エラーを戻り値で表す場合はどうでしょうか?これは、if文やswitch文を用いて処理コードを分離することができます。確実に構造化例外処理より劣ります。構造化例外処理なら、例えば例外をスローする可能性のあるメソッドを呼び出す時、例外が発生したとしても呼び出し元で特に処理する必要がなければ、呼び出し元には例外に関するコードを一切記述する必要がありません。例外はそのまま上位へと伝わり、適切な箇所で処理が行われます。]例外はハンドルされなければCLRに検出されます。全てハンドルし、ログを残すといったことも可能です。僕の場合、この最上位のハンドラで例外を検出したら、「予期しない例外が発生しました。」というようなメッセージをエンドユーザーに見せて、裏ではログを残すという手法をよく使います。エラーが戻り値で帰ってきた場合、そのエラーを適切に処理しなくても、CLRには当然検出されません。すると、どこかで不整合が生じます。ンがおかしな挙動を起こしたとしても、何が原因なのかは例外に比べて判別がつきづらくなります。正常な動作を続けてしまうかもしれません。その場合、バグを検出することは困難になります。]デバッグの最中にハンドルされない例外が発生した場合、その例外の各プロパティを参照すれば色々な情報を得ることができます。いった情報となります。先ほど書いたように、ハンドルされなかった例外が発生したら例外の情報をログに残すようにすれば、運用時に発覚したバグも調べやすくなります。エラーを戻り値で表した場合、これらの情報を取得するのは言うまでもなく大変困難です。】ここまでで、例外の利点について見てきました。これらは全て、業務エラーを例外で表した場合にも適用される利点です。ここからは、業務エラーとしての例外について別の視点から考察していきます。]例えば、「DBへの反映を行うメソッド」があったとします。このメソッドは「DBへの反映を行うメソッド」であり、「DBへ反映できるようなら反映するメソッド」ではありません。他者が先にDBを更新していた場合、DBへの反映は行えず、業務エラーとなります。業務エラーは、一般的に想定されるエラーと見なされます。想定されるエラーだからと言って戻り値で返してはいけません。想定されていようがいなかろうが、DBへの反映を行うメソッドでDBへの反映ができないのであれば、それは紛れもない「例外的事象」なのです。「じゃあメソッド名を変えれば良い」と思うかもしれません。いうメソッド名でしょうか?でもこれ、一つのメソッドで二つの機能を表してますね。構造化の観点からすると、一つのメソッドが複数の機能を表すのはよろしくありません。実行用のメソッドの他に検証用のメソッドを用意すればいいかというと、一概にそうとは言えません。検証メソッドは検証結果を戻り値で返します。]例外にはコストがかかります。基本的には問題にならないはずです。この時、他者が既にDBの更新を行っていたために更新に失敗した場合、これは業務エラーとなります。頻繁に発生しない業務エラーで多少レスポンスが悪くても、それは問題にはならないでしょう。頻繁に発生する業務エラーがあったとします。これも問題にはなりません。2-1にて前述したように、「例外発生のコストによるパフォーマンスの低下が無視できない状況」では、検証用のメソッドを用意するからです。エンドユーザーからの入力値の妥当性検証は必ず行います。入力値の妥当性検証は、パフォーマンスの向上以外にも、可読性の大幅な向上にも繋がります。場合、それは業務エラーではなくシステムエラーとなります。クラス、またはその派生クラスとなります。】ここまでで、業務エラーを例外で表すことの素晴らしさを語ってきました。次に、業務エラーを例外で表す際の注意点をいくつか挙げます。]判別情報、つまり例外の場合は型です。各業務エラーに応じた適切な例外クラスを用意し、それぞれをハンドルできるようにします。業務エラーが適切に処理されている限り利用することはありません。プロパティをそのままエンドユーザーに表示したりしてはいけません。これらは業務エラー・システムエラー問わず言える事です。システムエラーの場合、SQLException.Numberプロパティなど、型以外に判別情報を提供している場合もありますが。]Microsoftは以前、アプリケーション固有の例外はApplicationExceptionから派生させることを推奨していました。現在はこれを推奨していません。変わりにExceptionから派生させることを推奨しています。?これは、Microsoftに原因があります。Microsoftはこのようなルールを推奨しようとしていました。自身がこのルールを守れなかったのです。実は、FCLに用意されている例外の中には、ApplicationExceptionから派生しているクラスや、Exceptionクラスから直接派生しているクラスが紛れ込んでいます。FCLがルールを守れていない以上、このルールはもう台無しです。ApplicationExceptionから派生させる意味はなくなります。業務エラーは例外としてスローし、上位で適切な処置を行う必要があります。業務エラーを例外で表すことにより、可読性・保守性の向上に繋がります。業務エラーを例外で表しても、適切な設計を行えばパフォーマンスに問題はありません。業務エラーを表す例外に対して適切な処理を行っている限り、型以外の例外情報は不要です。業務エラーを表す例外がハンドルされなかった場合、例外情報がとても重宝します。クラスから派生させます。第2版この記事を書くに当たって、この書籍の例外に関するページが大変参考になりました。業務エラー・システムエラーという観点では特に書かれていませんが、是非一読してみることをお勧めします。>>コメント一覧(いつも参考にさせて頂いております。コメントさせて頂きます。)私も.NETの例外については随分と悩まされました。Javaでの例外の扱いはもっとすっきりしていますが、.NETでの業務エラーは、やはり"戻り値"で扱わなければならないと思われます。今回の事例については、私としては"業務エラー"ではなく元々"例外"として扱われるべきものではないかと思います。SQLException.Numberは、逆に例外の一部を業務エラーとして処理できるようにある機能だと思います。ServiceのSOAPExceptionの存在です。SOAP"機能拡張"でも使用しなければAPPサーバで発生した例外を一元的にハンドリングすることができません。すべて例外で処理することを想定しているのなら、"機能拡張"を使用しなくてもよいように提供(設計)されるはずだからです。外部キー制約違反や、一意キー制約は業務フロー上想定しているかで、例外とも、業務エラーとも扱われると赤間さん、AAfNは暗に言っていると解釈しています。正常系、業務エラー合わせてフォローするのが"例外"と考えます(システム上はエラー(例外)を想定するわけですが)。業務エラーも基本的には検証用メソッドの形を取るものだと思います。今回の事例については、私としては"業務エラー"ではなく元々"例外"として扱われるべきものではないかと思います。挙げた楽観同時実行制御の例のことでしょうか?これはもしかしたら人によって意見の別れる所かもしれません。SQLException.Numberは、逆に例外の一部を業務エラーとして処理できるようにある機能だと思います。すべて例外で処理することを想定しているのなら、"機能拡張"を使用しなくてもよいように提供(設計)されるはずだからですう~ん、Soapの仕様がそうだったからという理由は、少々危険な気がします。正常系、業務エラー合わせてフォローするのが"例外"と考えます(システム上はエラー(例外)を想定するわけですが)。業務エラーも基本的には検証用メソッドの形を取るものだと思います。ご返答ありがとうございます。>(割愛)でも、検査例外がないから業務エラーを例外で表してはいけないというのは、どうかなと思うんです私が第一に考えていることは、『予め想定(定義)された業務エラーには例外を使用しないということ』は、.NET上の制約ではないかということです。ゆえに、(現在は).NETの機能を最大限活かすためにそれに沿って考えようとしています。例外の持っている機能(良さ)を否定しようと思っているわけではありません。『例外(Exception)脳の作り方』にかぶれてそれを無理やり.NETに持ち込もうと格闘していた時期があったくらいですので(笑)。.NETの"例外"はシステム上(論理上)では想定されるエラーも含まれますが、予め想定されないエラーを処理するためにあるものと理解しています。"業務エラー"というものは予め想定されていますので、フロー上にも記述でき、広義でいえば正常系に含まれるものと言えます(考えます)。>(割愛)結局は処理漏れが発生する可能性があることには変わりませんその通りだと思います。そのような可能性のある箇所に対しては、正常系(処理)、業務エラー(処理)を包含する形で例外(処理)を施すと考えます。業務エラーは、予め想定されていますのでもともと多くの情報を必要としないはずです。今回の事例については、私としては"業務エラー"ではなく元々"例外"として扱われるべきものではないかと思います。正しくは、"例外"と断定せず、楽観同時実行により起こる状況を"業務エラー"として扱うかというビジネス上の判断により、SQLExceptionでキャッチして、"業務エラー"として処理すべきか、そのまま"例外"として処理すべきか決まるものだと思います。>(割愛)型情報ではなくプロパティで提供することによるメリットは僕には見出せません恐らく、上記のような場合で使用されるものと考えます。私はSqlExceptionクラス設計者が何を意図してそのように実装したのかを考え(想像し)、それを最大限有効に活用しようと考えています。仮に、それが私個人の意に反し、またスマートではないと思えても、です。#クラス設計思想を暴露するような本があれば、面白いと思うのですけれど。そのように.NETのクラスを観察すると理路整然としない、人間くささを感じることが時々ありますが、それはそれで便利だと理解することがほとんどです。>(割愛)判別情報を指定することが可能です失礼致しました。例としてはあまりよくなかったと思います。最近Webサービスを結合にに使用することが多かったものですから。#ご指摘を受けて思い出しました。「型」の代わりに「エラーコード」を使うというのはどうでしょうか、ともめました(苦)。後は戻り値の間違いでしょうか?フォローとは、try{}でくくるという意味です。私は.NETではこのよう扱えと考えています(ほぼ引用)。業務設計で想定されている範囲で処理が分岐する正常終了でないケース。業務設計で想定しない異常のため正常終了できないケース。設計時に「業務エラー」と「アプリケーション/システムエラー」の分類を的確に行い、その上で実装しなければならない。状況によっては、「業務エラー」を「アプリケーション/システムエラー」に切り替えたり、「アプリケーション/システムエラー」を「業務エラー」に切り替えるコードが必要になる場合がある。キャッチした例外から戻り値を返すことも(その逆も)ありだと考えています。.NETの"例外"はシステム上(論理上)では想定されるエラーも含まれますが、予め想定されないエラーを処理するためにあるものと理解しています。"業務エラー"というものは予め想定されていますので、フロー上にも記述でき、広義でいえば正常系に含まれるものと言えます(考えます)。この点に関して、「更に検証メソッドを必ず用意して業務フローを確立する」というのであればまだわかるのですが、戻り値で表すというのは完全にNGです。この理由一つで戻り値を否定する充分な根拠になると考えますがどうでしょう?また、例外とは、操作レベルの例外的事象を表すのであって、業務フローどうこうは無関係と考えます。最終的にはプレゼンテーション層で適切にハンドルされ、「業務フロー」に乗っかるわけですし。そのような可能性のある箇所に対しては、正常系(処理)、業務エラー(処理)を包含する形で例外(処理)を施すと考えます。どのような処理を施すのでしょうか?戻り値を判定するswitchブロックのdefault句で例外をスローするとかでしょうか?例えば、当初業務エラーが発生しないと考えていたメソッドで、業務エラーが発生するとわかり、戻り値の型をvoidから列挙体Aにしたとします。それを受け取って処理する側の変更作業が抜けてしまった場合、処理の施しようがないかと思います。書きましたように困難なことです。業務エラーは、予め想定されていますのでもともと多くの情報を必要としないはずです。例外は、処理漏れが生じた際にそれを検出しやすくするようにと考えられたものだと思います。その際、その処理漏れについての詳細な情報を提供するために、例外は色々な情報を保持しています。処理漏れの可能性がある以上、例外の持つ情報が不要となることはないと考えます。第2版ですね^^;ちなみにこの本では、例としてある口座からある口座へ指定した金額を移動させるメソッドを取り上げています。戻り値の型はvoidとし、「戻った場合は成功、失敗した場合は例外をスロー」としています。移動元の口座の残高が不足しているためお金を移動できない等のケースは例外として扱うと明示しています。正しくは、"例外"と断定せず、楽観同時実行により起こる状況を"業務エラー"として扱うかというビジネス上の判断により、SQLExceptionでキャッチして、"業務エラー"として処理すべきか、そのまま"例外"として処理すべきか決まるものだと思います。うーん、そもそもAAfNと赤間本では業務エラーの考え方が違うんですよね。AAfNでは、想定される・されないという区別はしていません。見てもらえばわかるとおり、私も想定される・されないという区別はしていません。赤間本を読んで「業務エラーを戻り値で表そう」という意見を持たれている方々は、恐らく想定する・しないで業務エラーを区別しているかと思います。それを少し取り上げました。「業務エラーは一般的に想定されるエラーと見なされる」という言い回しもそのためです。そもそも想定される・されないというのは、処理を行うメソッドが判断するのではなく、利用者側が判断することです。ビジネス層をコンポーネント化し、これをいくつかのアプリケーションから利用するとします。この時、その状況を想定する・しないは各アプリケーションに委ねられます。何がいいたいかというと、その状況が想定されるべき状況であるかどうかを決めるのは各アプリケーションであって、ビジネスコンポーネントが決めていいことではないんじゃないでしょうか?この例えはわかりやすく説明するためにビジネス層をコンポーネント化した場合を取り上げましたが、当然コンポーネント化に関わらず言える事です。通りすがりですが、意見など。基本的には「業務エラーは例外で返す」に一票です。根拠としては以下の二点です。・業務ロジックは処理結果を返すべき・関数は一値のみを返すべきつまり赤間先生の「業務エラーは返り値で返す」という案には、「じゃあ処理結果は何で返すの」と反論したいわけです。outパラメータで返す、や処理結果と業務エラーの二値を返す関数にする、などがありますが、あまりスマートには感じられません。赤間先生の「業務エラーは返り値で返す」という案には、「じゃあ処理結果は何で返すの」と反論したいわけです。outパラメータで返す、や処理結果と業務エラーの二値を返す関数にする、などがありますが、あまりスマートには感じられません。赤間本では具体的な実装例が載っていませんでしたが、それらの方法以外思いつかないですね。メソッドなら戻り値をそのまま使えますが・・・。]メソッドが業務エラーを例外で返すのなら,そのことを示す属性を付けておいてほしい。outパラメータはスマートでないというが,リソースが絡んでくるときは,outパラメータでないと泣きを見ますよ。そのことを示す属性を付けておいてほしい。属性を付けるということは何らかの処理を付加するということだと思うのですが、その辺りを詳しく聴かせて頂きたく思います。コメントを使うべきと思いますがいかがでしょう。]自分も「業務エラーは例外で返す」に一票です。例外という便利な機構を使わない理由がわかりません。C#に欠陥があるなら別ですが・・。使い方の一つを考えました。場合は、専用のメッセージエラー・ダイアログを出す例です。----------------------------すみません。コメント欄に綺麗にソースコードを張り付けるには、どうしたらいいですか・・?実体参照で、インデントを空けました。ありがとうございます。さん、コメントありがとうございます。試したことないので何とも言えませんが、ソースコードのインデントを保持しての貼り付けは無理かもしれません。使用したりでいけますね。さん実はその方法は昔やったことがあるのですが、今はその方法にはどちらかというと否定的な考えを持ってます。格納するのはあまり良くないです。レイヤで扱うべきだからです。(例えば、画面の背景色をビジネスロジックが直接操作しているようなものです。)これにより、自作例外共通の基底クラスを用意してそれをグローバルなエラーハンドラで処理するという方法は自然と取れなくなります。ユーザーに表示するエラーメッセージを例外が持たないと言うことは、例外の発生した状況や例外の型に応じて適切なエラーメッセージを表示するように制御しなければならなくなるからです。他にも、メッセージボックスではなくラベルにエラー表示する場合はグローバルなエラーハンドラが使えません。いうことで、グローバルなエラーハンドラは、予期せぬエラーを拾って通知するという目的でのみ使用するのが良いと思います。「予期せぬエラー=仕様で対応していない異常系やバグ」ですので、メッセージボックスしか使えなくても別に良いと考えます。29ブログ内検索カウンターアクセスカウンターおすすめプロフィールAuthor:よこけん性別は男です。生年月日は1984年2月頃です。社会人は2004年4月頃からやってます。C#は2004年10月頃からやってます。このブログは2006年7月頃からやってます。アプリケーションリンクopus-i商用可・加工可で、綺麗な素材を提供していらっしゃいます。紹介していらっしゃいます。中心に扱っていらっしゃいます。コマンドレットをまとめたスナップインです。クラスライブラリを作ってみました。最近の記事ブログ移転しました。返事を返せない場合があります。