誰もエラーを処理できません

ある本から別の本へ、記事から記事へ、表現という意見



try { //do something } catch(Exception ex) { }
      
      





悪い習慣です。 コードを返すことも悪い習慣です。 しかし、私たちプログラマーがこの知識で生きることは容易になりつつあり、本当に議論の余地はありませんか? そして、最もおもしろい質問は-アプリケーションの間に発生するエラーを正しく処理する方法を世界中のだれでも知っていますか? (これにより、実際に起こったことに対応するエラーメッセージを処理して出力するのが理にかなっているエラーのみの処理を理解し、ユーザーを混乱させませんが、理想的には問題の解決策を提供します)。



著者は最良のアプローチを知らないため、この記事の目的は既存の概念を「広める」ことではありません。 この記事の目的は、例外処理の問題を悪化させ、「 catch(Exception)



しないcatch(Exception)



」または「例外を常にスローする」スタイルで単に登録を解除するだけでは不十分であるという事実を認識することです。 これは役に立ちません。 ロシア語では、この主題についてほとんど書かれていません(ロシア語を話す著者を信頼するのは誰ですか?冗談ですが、あらゆる冗談に冗談があります)。 この記事は、経験するのが賢明ではない開発者も対象としているため、読者は、「この世界で最も強い」ものを含め、間違いなくすべての人がエラー処理に苦しんでいることを認識し、問題を少し深いレベルで理解し始めます。



最初に、2つの記事の翻訳を紹介したいと思います。

1. チェック例外の問題。 Anders Halesbergとの会話。 パート2 :2003年8月18日

2. 検証不可能な例外のある生活。 2014年3月3日。



また、簡潔にするために、これらの記事のすべてを完全に翻訳したわけではないことにも言及したいと思います。 これらは、特定のトピックに関して最も重要な切り抜きです。 私は次のように自分からテキストを組み立てます: Spock speech text Spock speechの 終わり



チェック例外の問題。



Bruce Eckel :C#にはチェック例外のメカニズムがありません。 C#でチェック例外を有効にするかどうかの決定はどのように行われましたか?

Anders Hejlsberg :チェック済み例外には、スケーラビリティとバージョン管理という2つの問題があります。

Bruce Eckel :以前は、チェック例外はクールだと思っていました。

Anders Hejlsberg :そのとおりです。 正直なところ、表面的な検査で、彼らは本当によく見えます、そして、この考えですべてがうまくいくようです。 例外メカニズムが素晴らしい機能であることに絶対に同意します。 特定の実装だけでは問題になる可能性があります。 このメカニズムをJavaで実装された方法で実装すると、問題のセットを別のものと単純に交換できると思います。 その結果、人生が楽かどうかは私には明らかではありません。 私たちは人生を変えます。

Bruce Eckel :チェックされた例外に関してC#開発チームで意見の相違はありましたか?

Anders Hejlsberg :いいえ、私たちの言語設計チームにはもっと合意があったと思います。 C#は、チェックされた例外に関してはサイレントです。 いつか最良の解決策が見つかった場合-そして私を信じて、私たちはこの問題について考え続けます-私たちは戻って必要なものを留めることができます。 私は、芸術を進歩させるということを何も言わないなら、独自のフレームワークを作成しようとするよりも、黙って中立の立場を維持する方が良いという事実の堅実な支持者です。

Bruce Eckel :極端なプログラミング開発者は、「最も簡単に機能させる」と言います。

Anders Hejlsberg :はい、アインシュタインは言った、「それは最も簡単だが、簡単ではない」。 チェックされた例外はプログラマにとって手錠だと思います。 多くの潜在的な例外を宣言する新しいAPIを使用しているプログラマーを観察し、コードがどのように乱雑になるかを確認できます。 その結果、チェックされた例外はそれらに対して何もしないことに気付きます。

Bill Venners :あなたは、チェックされた例外に関するスケーラビリティとバージョナビリティについて言及しました。 これらの概念に関連する2つの問題の意味を説明していただけますか?

Anders Hejlsberg :バージョニングから始めましょう。ここの問題は見やすいからです。 例外A、B、Cのスローを宣言するメソッドfoo



を作成するとしましょう。メソッドの2番目のバージョンでは、いくつかのチップを追加し、 foo



メソッドは例外Dをスローできます。このメソッドの既存のユーザーはほぼ100 %はこの例外をキャッチしません。

新しい例外を追加すると、クライアントコードが破損します。 インターフェースにメソッドを追加するようなものです。

Bill Venners :しかし、言語にチェックされた例外がなくてもクライアントコードを壊していますか?

Anders Hejlsberg :いいえ。多くの場合、人々は「酔っ払っている」からです。 これらの例外のいずれもキャッチしません。 最下位レベルでは、メッセージループの周りに例外ハンドラーがあります。 このハンドラーは、何か問題が発生したことを示すウィンドウを表示するだけです。 プログラマーは、すべてのコードをtry\finally



でカバーすることでコードを保護しているため、例外の処理を避けることができます。これは、処理できない例外です。

Bill Venners :一般的に、最も一般的なケースでは、ユーザーは明示的に例外を処理するためにスタックの最上位のハンドラーを好むと思いますか?

Anders Hejlsberg :例外との関係の最も重要な部分が例外を扱うことだと人々が考えるのは面白いです。 これはそれほど重要ではありません。 よく書かれたアプリケーションでは、 try\finally



to try\catch



構文の比率は約10対1です。

ビル・ヴェナーズ :それで、結果はどうですか?

Anders Hejlsberg :結果として、あなたは例外から身を守り、それらを処理しません。 他の場所で例外処理を実装します。 当然、あらゆるタイプのイベント指向アプリケーションでは、最新のUIアプリケーションの場合と同様に、メッセージループの周囲に例外処理を実装し、ここで処理するだけです。 ただし、プログラム中は、キャプチャされた割り当てられたリソースを解放するなどして、自分自身を保護します。 常に一貫した状態にあるように、自分の後を掃除します。 100の異なる場所で例外を処理し、エラーメッセージを含むウィンドウをスローするプログラムを作成する必要はありません。

例外処理は一元化する必要があり、例外がハンドラーに伝搬される間は自分自身を保護する必要があります。



スポックスピーチ

スケーラビリティに関する問題は、Anders Halesbergによって提案された例外処理の意味を変更しないため、このトピックの観点から翻訳をレイアウトすることはお勧めできません(興味のある人なら誰でも調べて見ることができますが、一般的には、プログラムの成長とともに、放出される数例外が成長している、と誰もが、すべてのダースまたは例外の二つの異なるタイプを通過することができ、特に方法をそれらを処理することができなくなり、あなたのハンドラは、地獄のfootclothsに変わりますtry\catch



と、コードがサポート不可能になります )こと。

最近では、2014年3月3日、Eric LippertがブログでC#の例外処理のトピックも取り上げました。

彼は議論を2つの部分に分けました。最初の部分ではいくつかの質問をし、2番目の部分では答えを集めて履歴書を作成しました。

それで、エリックが彼のブログの読者に尋ねた質問:

スポックスピーチの終わり



未確認の例外のある生活







スポックスピーチ

3番目の質問はまだ尋ねられましたが、質問はあまり面白くなく、それに対する回答も特に興味がないので、対応する部分は省略されます。

スポックスピーチの終わり



読者のコメントからの主な結論:例外はC#に少し混乱を加えます。 言語のセマンティクスと例外階層の組織(または組織の欠如)により、どの例外をキャッチし、どの例外をスキップするかを知ることが難しくなります。 多くの読者が多くの素晴らしいコメントを残しましたが、そのうちの1人は私に最も感銘を受けました:

「処理」例外の概念全体は、愚か者のためのゲームに少し似ていると思います。 特定の種類の例外を実際に処理し、ハンドラーでインテリジェントな処理を実行できた回数は、おそらく片手の指で数えることができます。 99%のケースでは、すべてをキャッチするか、何もキャッチしません。 いずれかのタイプの例外がスローされた場合、安定した状態を復元してから、プログラムの実行を継続または中断します。


これは失礼ですが、公正だと思います。

このコメントは、ポケモンの処理を想定しています-それらをすべてキャッチします! ( すべてをキャッチする必要があります )解決策です。 コメンテーターのほぼ3分の1がcatch(Exception)



使用に対するサポートを表明していることに驚きました。 私が通常言うように、C#は「 成功の落とし穴 」になるように設計されており、最も単純なものが最も正しいものです。 この場合、この注目すべき目標は達成されていないようです。 catch(Exception)



が最小の真のパスであり、最も簡単なパスである場合、正しいパスが重すぎるためです。

圧倒的多数のコメンテーターは、バグが修正されたと書いています。その理由は、特定のタイプの例外のcatch



不足でしたが、そのようなケースは「1〜2回」、「ときどき」、「頻繁」などの異なる頻度で異なる人々に起こりました。

3番目の回答者は、MSDNおよび他の形式のドキュメント(XMLコメントを含む)を使用して、キャッチする必要のある例外の種類を決定したと述べました。 MSDNは賞賛され、批判されました。 MSDNの一部は完全に記述されており、他は明確ではないように記述されています。 ドキュメントの3番目の部分は徹底的に批判されました;そのようなドキュメントを誰も信じていません。

四半期は、どの例外をキャッチすべきかを見つけるために、デバッグ、テスト、ログの読み取り、クラッシュダンプの受信など、試行錯誤のようなものを使用したと述べました。

繰り返しになりますが、失望は次のコメントによって要約されました。

try/catch



ブロックはフラストレーションの練習になります。動作中にすべてが故障するまで、適切なタイプの例外をキャッチすると思うからです。

非常に多くのコメンテーターが、例外処理システムが例外タイプ自体を最も重要な情報と想定していることに気づきましたが、1つのタイプでは例外を適切に処理するには不十分です。


スポックスピーチ

突然! 直接的または間接的に言われるほとんどすべてが、ベストプラクティスと矛盾しています。

別の問題をスローします:何か問題がある場合に例外を常にスローするループでコードを呼び出す必要がある場合はどうでしょうか? 古い非生産的なマシン(そしてロシアには非常に多くのマシンがあります)は死ぬでしょう。 呼び出されたメソッドの所有者である場合は、必要な情報を含むリターンコードまたはクラスインスタンスが助けになります。 予想どおり、リターンコードは無効ではありません。

また、 catch(Exception)



から逃れることはできません。特定の状況で例外がシステム全体を飛ぶように思わない限り(例外が飛ぶほど)、メッセージループレベルだけでなく、あらゆる場所でcatch(Exception)



catch(Exception)



する必要があります、彼らが食べるより多くのリソース)。

catch(Exception)



1つのキャッチが残っていcatch(Exception)



StackOverflow



またはOutOfMemory



をキャッチし、目をOutOfMemory



ことさえできます。これは、Hello、Worldを書かない場合、数百万ルーブル(またはドル)を支払うことができる悲しい結果につながります!

示された問題の「解決策」(エラー処理の問題に対する既存の解​​決策はほとんど誰も完全に満足させないか、完全に満足させるため、意図的に引用符で囲みます)には、フィルタリングが適しています。 ところで、MSDNで例外フィルタリングはベストプラクティスではないと認識されているにもかかわらず、例外処理を担当するEnterprise Frameworkの部分は、対応するポリシーで構成されたフィルタリングに基づいています。

次に、例外処理を簡素化する単純な静的クラスを示します。

 public static class Exceptions { private static readonly List<Type> fatalExceptions = new List<Type> { typeof (OutOfMemoryException), typeof (StackOverflowException), //  ,        }; public static string FullMessage(this Exception ex) { var builder = new StringBuilder(); while (ex != null) { builder.AppendFormat("{0}{1}", ex, Environment.NewLine); ex = ex.InnerException; } return builder.ToString(); } public static void TryFilterCatch(Action tryAction, Func<Exception, bool> isRecoverPossible, Action handlerAction) { try { tryAction(); } catch (Exception ex) { if (!isRecoverPossible(ex)) throw; handlerAction(); } } public static void TryFilterCatch(Action tryAction, Func<Exception, bool> isRecoverPossible, Action<Exception> handlerAction) { try { tryAction(); } catch (Exception ex) { if (!isRecoverPossible(ex)) throw; handlerAction(ex); } } public static bool NotFatal(this Exception ex) { return fatalExceptions.All(curFatal => ex.GetType() != curFatal); } public static bool IsFatal(this Exception ex) { return !NotFatal(ex); } }
      
      





使用例:

 Exceptions.TryFilterCatch(host.Close, Exceptions.NotFatal, ex => logger.Error("    .", ex)); private bool TryGetTpmProcess(out Process process) { process = null; try { process = Process.GetProcessById(App.TpmProcessId.GetValueOrDefault()); return true; } catch (Exception ex) { if (ex.IsFatal()) throw; return false; } }
      
      







TryFilterCatch



メソッドを使用TryFilterCatch



と、フットクロスを短いレコードに変換できTryFilterCatch



。 拡張メソッドも使いやすいです。 TryFilterCatch



使用したメソッドは、 ここTryFilterCatch



ます。



この問題に関する私のすべての研究の簡単な要約は、次の結論です:私たちは皆、より少ない悪を使用しますが、悪を取り除く方法を最小限に抑える方法を誰も知らないため、いずれにしても悪を選択します。 必要な悪とは、チェックされていない例外の概念を意味します。これは、C#(およびJavaでも)で何かがうまくいかなかったことをすべての人とすべてに通知するデフォルトの方法です。 )



したがって、「花崗岩に注がれた文」を信じないで、両方を見て、例外がアプリケーションを殺さないようにしてください。



スポックスピーチの終わり



All Articles