例外を使用せずに、C# コードでエラー通信と回復を行おうとしています。例を挙げると、Func A があり、Func B または Func C またはその他の関数によって呼び出すことができるとします。Func A は、再利用を念頭に置いて設計する必要があります。(このアプリケーションには、新しい機能が一定期間追加され続ける進化するライブラリがあります)
Func A が想定されていることを実行できない場合、int を返します。0 以外の値は失敗を示します。また、失敗の理由も伝えたいと思います。呼び出し元関数は、この情報を複数の方法で使用できます。
- ユーザーにエラーメッセージを表示できます。
- コンテキストにより関連性の高い独自のエラー メッセージが表示される場合があります。
- それ自体が、祖先の呼び出し元関数への失敗を示す int 値を返す場合があります。
- インテリジェントなアルゴリズムを使用して、エラーからの回復を試みる場合があります。
仮説として、他の関数が依存する関数は、ステータス コード、エラー メッセージ、データの状態を示すその他の変数など、適切なアクションを実行するために、呼び出し元関数に複数のことを伝える必要がある場合があります。すべてを区切られた文字列として返すと、呼び出し元の関数が文字列を解析せずに情報を取得できない場合があります (これは独自の問題につながるため、お勧めしません)。
それ以外の唯一の方法は、必要なすべての情報のメンバー変数を含むオブジェクトを返すことです。これは、各関数がその状態オブジェクトを持つ必要があるため、「状態」オブジェクトが多すぎる可能性があります。
この要件を最もエレガントな方法で設計する方法を理解したいと思います。コーディングの時点で、Func A は、呼び出し元の関数がエラーから回復するためのインテリジェンスを持っているかどうかわからない可能性があるため、例外をスローしたくないことに注意してください。また、例外を使用せずにそのような設計が可能である (同時にエレガントである) かどうかを確認したいと思います。
関数ごとにデータ オブジェクトを使用して通信することが唯一の方法である場合、それが専門的なライブラリの作成方法です。汎用データ オブジェクトは存在できますか? 将来的に新しい関数が追加される可能性があることに注意してください。これらの関数には、異なる状態変数、またはエラーに関するサポート情報が含まれる場合があります。
また、関数の戻り値は「状態」オブジェクトであるため、関数が返すはずの実際のデータを ref または out パラメータとして渡す必要がある場合があることにも注意してください。
このための設計パターンはありますか?
この質問を投稿する前に、次の記事を読みました。
http://blogs.msdn.com/b/ricom/archive/2003/12/19/44697.aspx
例外がスローされない場合、try/catch ブロックはパフォーマンスを低下させますか?
コードフロー制御や回復可能なエラーには例外を使用しないことを提案している他の多くの記事も読みました。また、例外のスローには独自のコストがあります。さらに、呼び出し元の関数が、呼び出された関数のそれぞれによってスローされた例外から回復したい場合は、各関数呼び出しを try catch ブロックで囲む必要があります。汎用の try catch ブロックでは次の行から「続行」できないためです。エラー行の。
編集:
いくつかの具体的な質問: 2 つの異なるデータベースを同期するアプリケーションを作成する必要があります。1 つは専有データベースで、もう 1 つは SQL Server データベースです。再利用可能な関数を別のレイヤーにカプセル化したい。
機能は次のとおりです。独自のアプリケーションは多くのデータベースを持つことができます。これらの各データベースからの情報の一部は、単一の共通 SQL Server データベースにプッシュする必要があります。専有アプリケーションのデータベースは、アプリケーションの GUI が開いているときにのみ読み取ることができ、XML を介してのみ読み取ることができます。
アルゴリズムは次のようになります。
- 独自のアプリケーションで公開されているデータベースのリストを読む
- データベースごとに、同期プロセスを開始します。
- このデータベースに現在ログインしているユーザーが同期権限を持っているかどうかを確認します。(注: 各データベースは、異なるユーザー ID を使用して開くことができます)。
- このデータベースからデータを読み取ります。
- データを SQL Server に転送する
- 次のデータベースに進みます。
このアプリケーションの開発中に、ReadUserPermission、ReadListOfDatabases など、いくつかの再利用可能な関数を作成します。
この場合、権限が存在しないことを ReadUserPermission が検出した場合、呼び出し元はこれをログに記録し、次に開いているデータベースに進む必要があります。ReadListOfDatabases が独自のアプリケーションとの接続を確立できない場合、呼び出し元は自動的にアプリケーションを起動する必要があります。
では、どのエラー状態を例外として通知し、どのエラー状態をリターン コードを使用して通知する必要があるのでしょうか?
再利用可能な関数は、呼び出し元が異なるエラー回復要件または機能を持っている可能性がある他のプロジェクトで使用される可能性があるため、留意する必要があることに注意してください。
編集:
ファンク A がファンク B、C、D、E、F、G を呼び出し、ファンク B が何らかのエラー状態で例外をスローした場合、ファンク A はこのエラーから回復でき、続行したいと考えています。残りの実行、つまりFunc B、C、D、...を呼び出します。例外処理により、これを「エレガントに」行うにはどうすればよいですか? 唯一の解決策は、残りのステートメントが実行されるように、try catch ブロック内で B、C、D などのそれぞれへの呼び出しをラップすることです。
次の 2 つのコメントもお読みください。
https://stackoverflow.com/a/1279137/1113579
https://stackoverflow.com/a/1272547/1113579
エラーの回復と残りのコードの実行が、パフォーマンスに影響を与えることなくエレガントに達成できる場合、私は例外の使用を嫌いません。また、わずかなパフォーマンスへの影響は問題ではありませんが、デザインはスケーラブルで洗練されている必要があります。
編集:
わかりました、「Zdeslav Vojkovic」のコメントに基づいて、私は今、例外の使用を考えています。
例外を使用する場合、例外を使用せずにリターン コードを使用するユース ケースを教えてください。注:関数が返すはずのデータではなく、戻りコードについて話している。リターンコードを使用して成功/失敗を示すユースケースはありますか、またはユースケースはありませんか? それは私がよりよく理解するのに役立ちます。
「Zdeslav Vojkovic」から理解した例外の使用例の 1 つは、呼び出し先関数が呼び出し元関数に何らかの条件を強制的に通知し、呼び出し元の実行を中断したい場合です。例外がない場合、呼び出し元は戻りコードを調べることを選択する場合としない場合があります。ただし、例外が発生した場合、実行を継続したい場合、呼び出し元の関数は必ず例外を処理する必要があります。
編集:
もう一つ面白いアイデアがありました。呼び出し元関数がエラーから回復するという考えをサポートしたい呼び出し先関数は、イベントを発生させ、イベントが処理された後にイベント データをチェックし、例外をスローするかどうかを決定できます。エラーコードは一切使用されません。回復されないエラーには例外が使用されます。基本的に、呼び出し先関数がコントラクトの内容を実行できない場合、使用可能なイベント ハンドラーの形式で「ヘルプ」を要求します。それでもコントラクトを実行できない場合は、例外がスローされます。利点は、例外をスローする追加のオーバーヘッドが削減され、呼び出し先関数またはその呼び出し元関数のいずれかがエラーから回復できない場合にのみ例外がスローされることです。
呼び出し元の関数がエラーを処理するのではなく、呼び出し元の呼び出し元の関数がエラーを処理したい場合、カスタム イベント ディスパッチャーは、イベント ハンドラーがイベント登録の逆順、つまり最後に登録されたイベントで呼び出されるようにします。 handler は、他の登録済みイベント ハンドラーより前に呼び出す必要があります。このイベント ハンドラーがエラーを解決できる場合、後続のイベント ハンドラーはまったく呼び出されません。一方、最新のイベント ハンドラーがエラーを解決できない場合、イベント チェーンは次のハンドラーに伝達されます。
このアプローチについてフィードバックをお寄せください。