個別の try-catch ブロックを使用せずに、コードのすべての行を Try-catch します。
-
02-07-2019 - |
質問
現在この問題は発生していません, 、しかし、それは決してわかりません、そして思考実験はいつでも楽しいです。
これを試みるにもアーキテクチャに必ず問題があるという明らかな問題を無視する, 他の人が設計したひどく書かれたコードがあり、同じコード ブロック内で広範で多様な操作を多数実行する必要があると仮定します。
WidgetMaker.SetAlignment(57);
contactForm["Title"] = txtTitle.Text;
Casserole.Season(true, false);
((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true;
100 を掛けます。これらのうちのいくつかはうまくいくかもしれませんが、他のものはひどく間違っているかもしれません。必要なのは、C# の「on errorresume next」に相当するものです。そうでないと、多くのコード行に try-catch をコピーして貼り付けることになります。
この問題にどのように対処してみますか?
解決
VB.NET でコードを記述することは明らかですが、実際には VB.NET には次のような機能があります。 エラー時は次へ再開, を作成し、DLL で C# にエクスポートします。それ以外はただの罰のための大食いです。
他のヒント
public delegate void VoidDelegate();
public static class Utils
{
public static void Try(VoidDelegate v) {
try {
v();
}
catch {}
}
}
Utils.Try( () => WidgetMaker.SetAlignment(57) );
Utils.Try( () => contactForm["Title"] = txtTitle.Text );
Utils.Try( () => Casserole.Season(true, false) );
Utils.Try( () => ((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true );
個別の適切な名前のメソッドにリファクタリングします。
AdjustFormWidgets();
SetContactTitle(txtTitle.Text);
SeasonCasserole();
それぞれが適切に保護されています。
私なら言います 何もしない.
そうです、何もしないでください。
あなたは私に次の 2 つのことを明確に認識しました。
- アーキテクチャが破綻していることがわかります。
- こんなくだらないことは山ほどある。
私は言う:
- 何もしない。
- グローバルエラーハンドラーを追加して、ブームになるたびに電子メールを送信します。
- 何かが倒れる(またはテストに失敗する)まで待ちます
- それを修正します(必要に応じてリファクタリングします) ページの範囲内で).
- 問題が発生するたびに繰り返します。
それほどひどい場合はすぐに解消されます。うん、それはひどいことだと思うし、そもそもバグ修正に苦労するかもしれないが、それによって次のことが可能になる。 必要なコードやバグのあるコードを修正する前に (大量の)量 実際に動作している可能性のあるコード どんなに見た目がダサくても。
戦争に勝ち始めると、(すべてのリファクタリングのおかげで) コードをより適切に扱えるようになり、勝利の設計についてより良いアイデアが得られるようになります。
すべてをプチプチで包もうとすると、おそらく時間がかかるだけで、問題の解決にはまだ近づきません。
フェイルファスト
もっと詳しく言えば、私は質問をしていると思います。例外がスローされた場合、コードを何も起こらなかったかのように続行する必要があるのはなぜでしょうか。特定の状況で例外が予想される場合は、コードの周囲に try-catch ブロックを記述して処理するか、予期しないエラーが発生した場合は、アプリケーションを中止、再試行、または失敗することを選択する必要があります。負傷したゾンビが「脳みそ」とうめき声を上げているように続けてはいけません。
これは、プリプロセッサがあると便利なことの 1 つです。例外を受け入れるマクロを定義し、簡単なスクリプトを使用してそのマクロをすべての行に追加できます。
したがって、これが C++ であれば、次のようなことができます。
#define ATTEMPT(x) try { x; } catch (...) { }
// ...
ATTEMPT(WidgetMaker.SetAlignment(57));
ATTEMPT(contactForm["Title"] = txtTitle.Text);
ATTEMPT(Casserole.Season(true, false));
ATTEMPT(((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true);
残念ながら、C/C++ のようなプリプロセッサを備えている言語はあまり多くないようです。
独自のプリプロセッサを作成し、それをビルド前のステップとして追加できます。完全に自動化したい場合は、実際のコード ファイルを取得し、独自に try/catch 要素を追加するプリプロセッサを作成することもできます (そのため、コードに ATTEMPT() ブロックを手動で追加する必要はありません)。 。ただし、変更すべき行のみを変更することを確認するのは難しい場合があります (ビルドを中断しないように変数宣言やループ構成などをスキップする必要があります)。
しかし、これらはひどい考えであり、決してすべきではないと思いますが、質問がありました。:)
本当に、これは絶対にやってはいけません。エラーの原因を見つけて修正する必要があります。エラーを鵜呑みにしたり無視したりするのはよくないことなので、 正しい ここでの答えは「バグを修正してください。無視しないでください!」です。:)
On Error Resume Next
これは C# の世界では非常に悪い考えです。同等のものを追加することもできません On Error Resume Next
実際にあなたを助けます。それは、より微妙なエラー、データ損失、場合によってはデータ破損を引き起こす可能性のある悪い状態に放置するだけです。
しかし、質問者に当然のことを言うと、グローバル ハンドラーを追加し、TargetSite をチェックしてどのメソッドが失敗したかを確認することができます。そうすれば、少なくともどの回線で発生したかを知ることができます。次の部分は、デバッガーが行うのと同じ方法で「次のステートメント」を設定する方法を試して理解することです。この時点でスタックが巻き戻されていないか、スタックを再作成できることを願っていますが、試してみる価値は確かにあります。ただし、このアプローチを使用すると、デバッグ シンボルを含めるためにコードを毎回デバッグ モードで実行する必要があります。
誰かが言ったように、VB ではこれが可能です。C#でも同じようにやってみてはどうでしょうか?信頼できるリフレクターを入力してください:
これ:
Sub Main()
On Error Resume Next
Dim i As Integer = 0
Dim y As Integer = CInt(5 / i)
End Sub
これを翻訳すると次のようになります。
public static void Main()
{
// This item is obfuscated and can not be translated.
int VB$ResumeTarget;
try
{
int VB$CurrentStatement;
Label_0001:
ProjectData.ClearProjectError();
int VB$ActiveHandler = -2;
Label_0009:
VB$CurrentStatement = 2;
int i = 0;
Label_000E:
VB$CurrentStatement = 3;
int y = (int) Math.Round((double) (5.0 / ((double) i)));
goto Label_008F;
Label_0029:
VB$ResumeTarget = 0;
switch ((VB$ResumeTarget + 1))
{
case 1:
goto Label_0001;
case 2:
goto Label_0009;
case 3:
goto Label_000E;
case 4:
goto Label_008F;
default:
goto Label_0084;
}
Label_0049:
VB$ResumeTarget = VB$CurrentStatement;
switch (((VB$ActiveHandler > -2) ? VB$ActiveHandler : 1))
{
case 0:
goto Label_0084;
case 1:
goto Label_0029;
}
}
catch (object obj1) when (?)
{
ProjectData.SetProjectError((Exception) obj1);
goto Label_0049;
}
Label_0084:
throw ProjectData.CreateProjectError(-2146828237);
Label_008F:
if (VB$ResumeTarget != 0)
{
ProjectData.ClearProjectError();
}
}
コードを書き直します。論理的に相互に依存するステートメントのセットを見つけて、1 つが失敗した場合に次のステートメントが意味をなさないようにし、ステートメントの結果を無視したい場合は、ステートメントを独自の関数に分割し、try-catch で囲みます。それを続けます。
これは、最も問題のある部分を特定するのに役立ちます。
@ JBキング私に思い出させてくれてありがとう。Logging アプリケーション ブロックには、イベントのトレースに使用できる Instrumentation Event が含まれています。詳細については、MS Enterprise ライブラリのドキュメントを参照してください。
Using (New InstEvent)
<series of statements>
End Using
この使用のすべての手順はログ ファイルに追跡されるため、それを解析してログが壊れた場所 (ex がスローされた場所) を確認し、重大な違反者を特定することができます。
リファクタリングが最善の策ですが、リファクタリングがたくさんある場合は、最悪の犯罪者を特定するのに役立つ可能性があります。
あなた できた gotoを使用しますが、それでも面倒です。
実は、私はしばらくの間、単一ステートメントの try-catch のようなものが欲しかったのです。これは、ログ コードの追加や、失敗した場合にメイン プログラム フローを中断したくない場合などに役立ちます。
linq に関連する機能の一部を使用して何かできるのではないかと思いますが、現時点ではそれを検討する時間がありません。ステートメントを匿名関数としてラップする方法を見つけられれば、別の関数を使用して try-catch ブロック内でそれを呼び出すことができます...しかし、それが今のところ可能かどうかはわかりません。
コンパイラにこのコードの式ツリーを提供させることができれば、各ステートメントを元のステートメントをラップする新しい try-catch ブロックに置き換えることによって、その式ツリーを変更できます。これは思っているほど突飛なことではありません。LINQ の場合、C# は実行時にユーザー コードで操作できる式ツリーとしてラムダ式をキャプチャする機能を獲得しました。
System.Linq.Expressions に "try" ステートメントがないという理由以外の理由がない限り、このアプローチは現在の .NET 3.5 では不可能です。ただし、DLR と LINQ 式ツリーのマージが完了すると、C# の将来のバージョンで実行可能になる可能性があります。
C# でリフレクションを使用しないのはなぜですか?コードを反映するクラスを作成し、行番号を個々の try/catch ブロックに何を入れるかのヒントとして使用できます。これにはいくつかの利点があります。
- ソースコードを実際に変更する必要がなく、デバッグモード中にのみ使用できるため、それほど醜さは軽減されます。
- C# を実装しながら、C# について興味深いことを学びます。
ただし、もちろん、他の作業のメンテナンスを引き継ぎ、例外を修正できるように例外を把握する必要がある場合を除き、これには反対です。書くのも楽しいかも知れませんが。
楽しい質問。とてもひどい。
マクロを使えばよかったです。ただし、これは C# の強力な処理であるため、プリプロセッサの作業や外部ツールを使用して行を個別の try-catch ブロックで囲むことで解決できる可能性があります。やりたくなかったのかどうかはわかりませんが、 手動で ラップするか、try-catch を避けたかった場合 全体的に.
これをいじって、すべての行にラベルを付けて、単一のキャッチからジャンプして戻ろうとしましたが、あまりうまくいきませんでした。しかし、 クリストファーはこれを行う正しい方法を発見しました. 。いくつかあります これについては、Dot Net Thoughts での興味深い追加議論があります。 そしてで Mike Stall の .NET ブログ.
編集:もちろん。の try-catch
/ switch-goto
リストされているソリューションは実際にはコンパイルされません。 try
ラベルは範囲外です catch
. 。このようなものをコンパイルするには何が足りないのか知っている人はいますか?
コンパイラの前処理ステップを使用してこれを自動化するか、ハックアップすることもできます Mike Stall のインライン IL ツール 何らかのエラー無視を挿入します。
(例外の調査に関する Orion Adrian の回答 次の命令を設定してみるのも面白いです。)
全体として、これは興味深く、有益な演習のように思えます。もちろん、どの時点で ON ERROR RESUME NEXT をシミュレートする労力がコードを修正する労力を上回るかを判断する必要があります。:-)
のエラーをキャッチします。 UnhandledException イベント アプリケーションの。そうすることで、未処理の例外も、送信者や開発者が合理的に判断できるその他の情報に関してログに記録することができます。
残念ながら、あなたにはおそらく運がありません。On Error Resume Next は、一般的にはあまり推奨されていない従来のオプションであり、C# に関する私の知識と同等のものはありません。
コードを VB に残し (OnError ResumeNext に対する特定のリクエストを考えると、それがソースのようです)、必要な新しいコードを実装する C# dll または exe とやり取りすることをお勧めします。次に、リファクタリングを実行してコードを安全にし、この安全なコードを C# に変換します。
未処理の例外を処理する方法の 1 つのアイデアとして、Enterprise Library の例外処理コンポーネントの統合を検討してください。
これが ASP.Net アプリケーションの場合、Global.asax には「Application_Error」と呼ばれる関数があり、ほとんどの場合に呼び出されますが、致命的な障害が発生する場合は通常は呼び出されません。
これを避けたい理由をすべて無視してください....
単に行数を抑える必要がある場合は、次のようなことを試すことができます。
int totalMethodCount = xxx; for(int counter = 0; counter < totalMethodCount; counter++) { try { if (counter == 0) WidgetMaker.SetAlignment(57); if (counter == 1) contactForm["Title"] = txtTitle.Text; if (counter == 2) Casserole.Season(true, false); if (counter == 3) ((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true; } catch (Exception ex) { // log here } }
ただし、呼び出しの結果を再利用しようとする場合は、変数のスコープに注意する必要があります。
各行を一度に 1 行ずつハイライトし、try/catch で囲みます。これにより、あなたが言及したコピーペーストが回避されます