「例外からのリファクタリング」pluralsight.com-C#での機能原理の適用-例外からのリファクタリング 代わりにResultオブジェクトを使用します。 enterprisecraftsmanship.com-機能的なC#:障害の処理、入力エラーの処理
要約すると、結果オブジェクトを返すよりも例外をスローすることをお勧めします。例外は、バグを知らせるためにのみ使用してください。このアプローチの引数は次のとおりです。
一方、戻り値は(少なくともC#では)無視できますが、例外は無視できません。
既存のエンタープライズアプリケーションをこの方向にリファクタリングすることは良い考えですか?それとも、より過激ではないアプローチがより良いものですか? (ValidateUserInput(string input)
のようなメソッドの戻り値の型を使用することで、 Vexing例外 を確実に回避することが理にかなっていると信じています)
エラー変数はアンチパターンまたは良いデザインですか? も同様の質問です。違いは、明らかに「アンチマジック値によるエラー」(エラーコードを返す、さらに悪いことに
null
)について話しているのではなく、明らかにアンチパターンであることです。私は、ウラジミール・ホリコフによって提示されたパターンについて話しています。これには、プリミティブエラーコードを返すだけのような同じ欠点はありません。 (例:結果オブジェクトには、例外と同様にエラーメッセージがあります)
例外をスローするよりも結果オブジェクトを返すことをお勧めします
これらは異なる状況で使用する必要があるため、これは悪いアドバイスです。例外が最も適切なときに結果オブジェクトを使用するのはよくありません。
(たとえば)プロセスがメモリ不足のためにValidateUserInput
がそのタスクを実行できない場合、例外をスローしますis正しいこと。入力がどちらか有効または無効であることを示す値を返すことは、私たちがそれを知らないため、明らかに間違っています。 「メモリ不足」と呼ばれる検証エラーは、入力のエラーではないため、誤解を招く可能性があります。 3つのケースを作成する:有効、無効、メモリ不足は奇妙です。
メソッドがタスクを完了できないときは、例外を使用して通知する必要があります。結果オブジェクトは、メソッドisがタスクを完了できる場合に使用されますが、結果はさまざまなケースに分類できます。
たとえば、Search()
メソッドは、一致が見つかった場合に大文字と小文字を区別するオプションタイプを返し、一致が見つからなかった場合に別のオプションタイプを返す場合があります。ただし、データベースが切断されている場合、タイムアウトなどが発生した場合は例外がスローされ、検索を実行できませんでした。
では、リンクされたブログ投稿の引数についてはどうでしょうalways例外の代わりに結果オブジェクトを使用するべきですか?
投稿は私が言語羨望と呼ぶ症状から私見を苦しめています。これは、C#の異なるコンテキストを考慮せずに、異なる言語からパターンをシューホーンしようとします。通常、両方の言語が最悪になります。考慮すべきいくつかのポイント:
これは、すべてのエラーを結果オブジェクトに変換することによって、意図された利点を実際に得ることができないことを意味します。例えば:
例外をスローするメソッドは「正直」ではありません。メソッドのシグネチャを見ても、メソッドが失敗すると予想されるかどうかはわかりません。
これが問題であると思われる場合、それはなくなることはありません。フレームワークとサードパーティのライブラリstillそれらがスローする可能性のある例外をアドバタイズしません。つまり、コードがアドバタイズされていない例外を渡す可能性もあります。
これを問題と見なす場合は、代わりにJavaのような言語を使用することを検討してください。この言語では、例外がチェックされています-または例外のない言語。
例外処理は、多くのボイラープレートコードを追加します。
結果値のエンコーディングエラーはもうありません!結果値は、呼び出しスタックをラップ、アンラップ、および渡すためのボイラープレートを必要とします-例外の場合、自動的に行われます(ボイラープレートなし)。
Rust with use-result-objects through through)のような言語では、必要なボイラープレートの量がすぐにわかるため、ショートカット構文(最初にtry
マクロ、次に?
演算子)がそれを軽減するために導入されました。 Haskellにはdo構文があります。 C#には同様のものはなく、マクロもないため、定型文に苦しむ必要があります。
記事はすべてをOnSuccess
呼び出しのチェーンで非慣用的なC#に書き換えることになり、単純化するはずの初期コードよりもはるかに複雑になります。 (この記事では、「前」のコードに不必要な冗長性があるため、「後」の例では魔法のようになくなっています。)
さらに重要なことに、コードが解決しようとした最初の問題であるトランザクションのロールバックは、using
- blockを使用することにより、慣用的な方法でよりエレガントに解決できます。 RollbackLastTransaction
の使用は競合状態に対して脆弱である可能性があるようです。
例外を使用してフローを制御する場合、「goto」セマンティクスがあり、コードの特定の行にジャンプできます。
制御フローの例外を使用することはC#ではアンチパターンとして認識されているため、これはレッドニシンです。それは彼らの目的ではありません。しかし、いずれにしても、例外をGOTOと比較することは、そもそもなぜGOTOが悪いと見なされているのかについての誤解です。例外がGOTOのようなものであれば、return
も同様です。
結論:希望する言語、またはタスクに最も適した言語を選択します。次に、選択した言語の設計原則とイディオムを理解し、それらと戦うのではなく、それらを有利に利用します。
例外と結果オブジェクトの両方のユースケースがあるため、コードの「クリーンさ」はユースケースに依存します。それはすべて、プログラムの実行を停止することがどれほど重要かによって異なります。
例外はプログラミングの手榴弾です。ピンを引いて投げます。アプリケーションが空を高く飛ばさないようにする唯一の方法は、それをキャッチして処理することです(ピンが爆発しないように、ピンを戻すことができるとしばらく想像します)。例外は、絶対に操作を続行できず、すぐに停止する必要があるというエラー条件が発生したことを伝えます。
結果オブジェクトは何か別の方法で通信します。 「1つ以上の問題が発生したため、続行する前に誰かが修正する必要があります」と彼らは言う。結果オブジェクトは、「問題はありませんでしたが、知っておくべきいくつかの小さな警告は...」と言うこともできます。結果オブジェクトの目的は、エラーと警告を追跡することです。ログファイルのような出力。 「results」オブジェクトを返す操作は、他のいくつかのプロセスが失敗から回復できるため、通常は失敗しても問題ありません。
結果オブジェクトはフィードバックを提供するため、別のプロセスまたは人がエラーを修正して、操作を再試行できます。例外は、データに実際の損傷が生じる前にプログラムの実行を停止するためのものです。
例外を使用することの例外は、例外がチェックされずに伝播することを許可することによって損傷(実または仮想)が行われる場合です。たとえば、アプリケーションをクラッシュするとデータが失われるになる場合は、例外をキャッチして結果オブジェクトを返す方がよいでしょう。さらに重要なのは、クラッシュしたプログラムが人や動物に身体的、心理的、または経済的な危害を及ぼす場合、例外をチェックしないままにしておくことは絶対に望まないことです。繰り返しますが、「結果」オブジェクトまたは「エラーコード」は正しい方法なので、エラーを修正して、操作を再試行できます。
例外をスローするメソッドは「正直」ではありません。メソッドのシグネチャを見ても、メソッドが失敗すると予想されるかどうかはわかりません。
[以前のバージョンの]と比較してJavaこれは.Netの最大の1つであると主張されましたfailings。Java例外が発生しますメソッドシグネチャ。Netはそうではありません。
例外処理は、多くのボイラープレートコードを追加します。
それが行われたときのみ間違って。
何かを実行できる場所に例外ハンドラを追加します便利例外は、例外を「漏らす」のではなく「良いアイデア」であるというだけではありません。 (この種のボイラーめっきは非常に悪い考えです)。
「後藤意味論」? OK。
「特定のコード行」-それほどではありません。
whereまたはifを知らない例外をキャッチする必要があります。ループなどから抜け出すためにそれらを使用している場合は、それらを誤用しています。
例外は、「あきらめた!」に相当するコーディングです。他の誰かが手伝ってくれることを願っています。
Cのように、例外をスローすることを期待しない言語がある場合、結果を返すことはおそらく問題ありません。しかし、.NetとJava expectあなたは例外をスローする必要があり、結果戦略をそれらに強制しようとすると、最終的にはそれらが独自の例外により、注意深く構築された呼び出しと戻りの構造がばらばらになります。