15

まず、免責事項: 私は他の言語での経験がありますが、まだ C# の機微を学んでいます。

問題について...私は、try / catchブロックを私に関係する方法で使用するコードを見ています。解析ルーチンが呼び出されると、エラー コードを返すのではなく、プログラマーは次のロジックを使用しました。

catch (TclException e) {
  throw new TclRuntimeError("unexpected TclException: " + e.Message,e);
}  

これは、同じエラーをスローする呼び出し元によってキャッチされます ...
... 同じエラーをスローする呼び出し元によってキャッチされます ...
..... 同じエラーをスローする呼び出し元によってキャッチされます...

約6レベルバックアップします。

これらすべての catch/throw ブロックがパフォーマンスの問題を引き起こしていると考えるのは正しいですか、それともこれは C# での合理的な実装ですか?

4

8 に答える 8

16

どの言語でも悪いデザインです。

例外は、それらに対処できるレベルでキャッチされるように設計されています。例外をキャッチして、もう一度スローするだけでは、時間の無駄になります(また、元のエラーの場所に関する貴重な情報が失われる原因にもなります)。

どうやら、そのコードを書いた人は、以前はエラーコードを使用していましたが、実際にどのように機能するかを理解せずに例外に切り替えました。1つのレベルでキャッチがない場合、例外は自動的にスタックを「バブルアップ」します。

また、例外は例外的な発生に対するものであることに注意してください。決して起こらないこと。これらは、通常の有効性チェックの代わりに使用しないでください(つまり、ゼロ除算の例外をキャッチしないでください。除数がゼロかどうかを事前にチェックしてください)。

于 2009-03-05T18:30:59.873 に答える
15

msdn:Performance Tips and Tricksによると、実際のスローが発生するまで、パフォーマンスの問題なしに try と catch を使用できます。

于 2009-03-05T18:36:37.940 に答える
12

(キャッチではなく)スローは高価です。

何か有用なことをするつもりでない限り(つまり、より有用な例外に変換してエラーを処理する)、catchブロックを入れないでください。

例外を再スローする(引数なしでステートメントをスローする)か、さらに悪いことに、キャッチしたばかりの同じオブジェクトをスローすることは間違いなく間違っています。

編集:あいまいさを避けるために:

再スロー:

catch (SomeException) {
  throw;
}

以前の例外オブジェクトから例外を作成します。ここで、ランタイムが提供するすべての状態(特にスタックトレース)が上書きされます。

catch (SomeException e) {
  throw e;
}

後者の場合は、例外に関する情報を破棄する無意味な方法です。そして、キャッチブロックのスローの前に何もなければ、二重に無意味です。さらに悪い場合があります:

catch (SomeException e) {
  throw new SomeException(e.Message);
}

これは、含まれているほとんどすべての有用な状態情報(最初にスローされたものを含む)を失います。

于 2009-03-05T18:31:14.600 に答える
2

一般に、例外のスローは .NET ではコストがかかります。単に try/catch/finally ブロックを持つだけではありません。そうです、既存のコードはパフォーマンスの観点から見て悪いものです。なぜなら、既存のコードがスローされると、元の例外が 5 ~ 6 個のスタック フレームを自然にバブルアップさせるのではなく、値を追加せずに 5 ~ 6 個の肥大化した例外をスローするからです。

さらに悪いことに、既存のコードは設計の観点から非常に悪いものです。例外処理の主な利点の 1 つは (エラー コードを返す場合と比較して)、あらゆる場所 (コール スタック内) で例外/戻りコードをチェックする必要がないことです。実際に処理したい少数の場所でそれらをキャッチするだけで済みます。例外を無視しても (戻りコードを無視するのとは異なり)、問題を無視したり隠したりすることはありません。これは、コール スタックの上位で処理されることを意味します。

于 2009-03-05T19:00:22.780 に答える
1

Try / Catch / Throwは遅いです-より良い実装は、値をキャッチする前にチェックすることですが、絶対に続行できない場合は、カウントされたときにのみスローしてキャッチすることをお勧めします。それ以外の場合は、チェックとロギングの方が効率的です。

于 2009-03-05T18:33:17.853 に答える
0

スタックのすべてのレイヤーが同じ情報で同じ型を再スローし、新しいものを何も追加しない場合、それはまったくばかげています。

それが独自に開発されたライブラリ間の境界で起こっていた場合、それは理解できます。ライブラリの作成者は、以前のバージョンの例外スロー動作をシミュレートする方法を理解する必要なく、後で実装を変更できるように、ライブラリからエスケープする例外を制御したい場合があります。

正当な理由がなくても、どんな状況でもキャッチして再スローすることは一般的に悪い考えです。これは、catch ブロックが見つかるとすぐに、throw と catch の間のすべての finally ブロックが実行されるためです。これは、例外を回復できる場合にのみ発生します。この場合、特定の型がキャッチされているため問題ありません。そのため、コードの作成者は、その特定の例外型に応答して、独自の内部状態の変更を安全に元に戻すことができることを (うまくいけば) 知っています。

そのため、これらの try/catch ブロックは、設計時にコストがかかる可能性があります。プログラムが乱雑になります。しかし、実行時に大きなコストがかかるのは、例外がスローされたときだけです。これは、スタックへの例外の送信がより複雑になっているためです。

于 2009-03-05T19:01:49.173 に答える
-1

例外は遅いので、使用しないようにしてください。

ここで私が与えた答えを見てください。

基本的に、Chris Brumme (CLR チームに所属していた) は、これらは SEH 例外として実装されているため、スローされたときに大きな打撃を受け、OS スタックを通過する際にペナルティを受ける必要があると述べています。これは本当に優れた記事であり、例外をスローしたときに何が起こるかについて詳しく説明しています。例えば。:


もちろん、例外の最大のコストは、実際に例外をスローするときです。ブログの終わり近くでこれに戻ります。


パフォーマンス。実際に例外をスローしてキャッチすると、例外には直接的なコストがかかります。また、メソッド エントリでハンドラーをプッシュすることに関連する間接的なコストが発生する場合もあります。また、コード生成の機会を制限することで、しばしば目に見えないコストが発生する可能性があります。


ただし、例外を伴う深刻な長期的なパフォーマンスの問題があり、これを考慮して決定する必要があります。

例外をスローしたときに起こるいくつかのことを考慮してください。

  • コンパイラによって出力されたメタデータを解釈してスタック トレースを取得し、スタックの巻き戻しをガイドします。

  • 各ハンドラーを 2 回呼び出して、スタックのハンドラー チェーンを実行します。

  • SEH、C++、マネージ例外間の不一致を補正します。

  • マネージド Exception インスタンスを割り当て、そのコンストラクターを実行します。ほとんどの場合、これにはさまざまなエラー メッセージのリソースの検索が含まれます。

  • おそらくOSカーネルを旅してください。多くの場合、ハードウェア例外を発生させます。

  • アタッチされているデバッガー、プロファイラー、ベクトル化された例外ハンドラー、およびその他の関係者に通知します。

これは、関数呼び出しから -1 を返すことから何光年も離れています。例外は本質的に非ローカルであり、今日のアーキテクチャに明白かつ永続的な傾向があるとすれば、それは、良好なパフォーマンスを得るためにローカルにとどまらなければならないということです。

例外は問題ではなく、パフォーマンスの問題もなく、一般的には良いことだと主張する人もいます。これらの人々は多くの一般投票を獲得していますが、彼らはまったく間違っています. Microsoft のスタッフが同じ主張をしているのを見たことがありますが (通常は、通常はマーケティング部門が専門知識を持っています)、馬の口からの結論は、それらを控えめに使用することです.

古い格言、例外は例外的な状況でのみ使用する必要があります。これは、他の言語と同様に C# にも当てはまります。

于 2009-03-05T19:02:42.993 に答える