7

編集:私は答えのコードを見てきました:それらのどれも私が望むことをしません(私はチェックしました)。ネイティブの C# で私がやりたいことを行う方法はないようです。.NETがサポートしていることを考えると、それは災害ではなく、残念なことだと思います(受け入れられた回答を参照)。

皆さんありがとう。


このような c# コード (デバッガー以外では実行されないテスト フレームワークの一部) があり、スタックの巻き戻された部分でコードをデバッグするのが非常に面倒になるため、実際に例外をキャッチしないようにしています。

Bool bad = true;
try
{
   MightThrow();
   bad = false;
}
finally
{
   if(bad) DoSomeLoggingOnFailure();

   //// Does not catch!!!! 
   //// exception continues to unwind stack.

   //// Note that re throwing the exception is NOT
   //// the same as not catching it in the first place
}

これを行うためのより良い方法はありますか?

解決策は、キャッチされていない例外に関して、デバッガーの下でそれとまったく同じように動作する必要があります。最初のチャンス例外が 1 つだけ発生し、catch ブロックではなく、例外が最初にスローされた時点でデバッガーが中断する必要があります。

具体的には、キャッチされていない例外でデバッガーが in side MightThrow を停止する必要があります。

以下は、デバッガーが正しい場所でブレークしないため、機能しません。

try { ... } catch { throw; }

そして、これはスタック情報を失う (また、間違った場所で壊れる) ため、機能しません。

try { ... } catch(Exception e) { throw e; }

Dscope(failure)でブロックを使用できることを知っていました

4

17 に答える 17

35

したがって、.NET では、求めていることは理論的には可能ですが、簡単ではありません。

CIL では、実際に5種類の例外処理ブロックが定義されています。、およびC# で使い慣れたもの、およびその他の 2 つtry:catchfinally

  • filter- ブロックに似ていcatchますが、任意のコードを実行して、型を照合するだけでなく、エラーを処理するかどうかを判断できます。このブロックは例外オブジェクトにアクセスでき、例外スタック トレースに対してcatchブロックと同じ効果があります。

  • fault- ブロックに似ていfinallyますが、例外が発生した場合にのみ実行されます。このブロックは例外オブジェクトにアクセスできず、例外スタック トレースには影響しません (finallyブロックと同様)。

filter一部の .NET 言語 (VB.NET、C++/CLI など) では利用できますが、残念ながら C# では利用できません。faultただし、ブロックを表現できる言語を CIL 以外に知りません。

ただし、IL で実行できるため、すべてが失われるわけではありません。理論的には、Reflection.Emit を使用して、ブロックを持つ関数を動的に発行し、fault実行したいコードをラムダ式として渡すことができます (つまり、try 部分用、fault 部分用など)。 (a) これは簡単ではありません。(b) これにより、現在得られているよりも有用なスタック トレースが実際に得られるとは思えません。

申し訳ありませんが、答えは「これが方法です」タイプのものではありませんが、少なくとも今はわかります! あなたが今していることは、おそらく最良のアプローチです。


質問で使用されているアプローチは「悪い習慣」であると言っている人に注意してください。実際にはそうではありません。ブロックを実装するcatchと、「例外が発生したときに例外オブジェクトで何かをする必要がある」と言い、実装するfinallyと「例外オブジェクトは必要ありませんが、前に何かをする必要があります」と言っています関数の終わり」。

実際に言おうとしているのが「例外オブジェクトは必要ありませんが、例外が発生したときに何かをする必要があります」という場合は、2 つの中間、つまりfaultブロックが必要です。これは C# では使用できないため、理想的なオプションはありません。したがって、再スローを忘れてバグが発生する可能性が低く、スタック トレースが破損しないオプションを選択することもできます。

于 2009-02-10T21:17:19.677 に答える
27

これはどう:

try
{
  MightThrow();
}
catch
{
  DoSomethingOnFailure();
  throw; // added based on new information in the original question
}

本当に、それだけです。最後は、例外が発生したかどうかに関係なく実行する必要があるものです。

[編集:明確化]

言及したコメントに基づいて、元のスタック トレースを変更せずに例外をスローし続ける必要があります。その場合は、私が追加した飾り気のないスローを使用します。これにより、例外がスタックを継続し、例外の一部を処理できるようになります。典型的なケースは、ネットワーク接続またはファイルを閉じることです。

[ 2回目の編集:明確化について]

具体的には、catch ブロックではなく、スローの元のポイント (MightThrow 内) で停止するために、キャッチされていない例外のデバッガーが必要です。

私は、デバッグにマイナーな価値を追加するためにベスト プラクティスを破ることに反対します (そして、これは部分的に例外を処理するためのベスト プラクティスです)。例外を簡単に調べて、例外がスローされた場所を特定できます。

[最終編集:あなたの答えがあります]

kronozは、あなたが求めていた答えを思慮深く提供してくれました。ベスト プラクティスを破らないでください。Visual Studio を適切に使用してください。例外がスローされたときに正確に中断するように Visual Studio を設定できます。この件に関する公式情報は次のとおりです。

私は実際にはその機能を知らなかったので、受け入れられた答えを彼に伝えてください. ただし、手動でデバッグするためだけに、ファンキーな方法で例外を処理しようとしないでください。あなたがすることは、より多くのバグに自分自身を開くことです。

于 2009-02-10T20:12:53.327 に答える
7

どうしたの:

try
{
   MightThrow();
}
catch
{
   DoSomthingOnFailure();
   throw;
}
于 2009-02-10T20:13:17.910 に答える
7

例外が発生した場合にのみ実行する必要があるコードについては、catch ブロックを使用します。

try
{
   MightThrow();
}
catch (Exception ex)
{
   // this runs only when there was an exception
   DoSomthingOnFailure();
   // pass exception on to caller
   throw; 
}
finally
{
   // this runs everytime
   Cleanup();
}
于 2009-02-10T20:13:39.190 に答える
4

これはあなたが望むものです。エラーが発生した場合にのみこのメソッドが呼び出され、「throw」ステートメントはコールスタックをそのままにして例外を再スローします。

try
{
   MightThrow();
}
catch
{
   DoSomthingOnFailure();
   throw;
}
于 2009-02-10T20:16:41.850 に答える
3

失敗した場合にのみ実行される "finally" ブロックは、"catch" (パラメーターなし) と呼ばれます。:-)

ここで、小さな注意点があります。特定の例外タイプに特化した「キャッチ」ケースが必要で、すべての例外に対して機能する汎用の「キャッチ」が必要な場合は、少しカスタム ロジックを実行する必要があります。

したがって、私は次のようなことをします:

  try
  {
    MightThrow();
  }
  catch(MyException ex)
  {
    // Runs on MyException
    MySpecificFailureHandler()
    // Since we have handled the exception and can't execute the generic
    // "catch" block below, we need to explicitly run the generic failure handler
    MyGenericFailureHandler()
  }
  catch
  {
    // Runs on any exception hot handled specifically before
    MyGenericFailureHandler()
    // If you want to mimic "finally" behavior and propagate the exception
    // up the call stack
    throw;
  }
  finally
  {
    // Runs on any failure or success
    MyGenericCleanupHandler();
  }
于 2009-02-10T20:21:19.780 に答える
2

「MightThrow」がスローしない例外だけをキャッチするのはどうですか?

Bool bad = true;
try
{
   MightThrow();
   bad = false;
}
catch (SomePrivateMadeUpException foo)
{ 
   //empty
}
finally
{
   if(bad) DoSomeLoggingOnFailure();   
}
于 2009-02-10T21:14:12.150 に答える
2

私が理解している方法であなたの要件を要約させてください。

  1. ログを記録するために、例外が生成されたときにのみ実行されるコードが必要です。
  2. デバッガーでテスト フレームワークを実行し、例外がスローされた時点で中断したいと考えています。

最初の要件を満たすには、パラメーターなしのキャッチ アンド スローを使用して、誰もが提案した方法でコードを記述する必要があります。

パラメーターなしのキャッチを使用しているときに 2 番目の要件を満たすには、未処理の例外があるときだけでなく、例外がスローされたときにデバッガーが中断するように構成できます。あなたはその方法を知っていると思いますが、回答を完全にするためにここに記載します.VSでは、デバッグ->例外->共通言語ランタイム例外->スローチェックボックスをオンにします。

アプリが多数の処理済み例外をスローすることがわかっている場合、それは選択肢にならない可能性があります。その時点で、最初の要件を満たすために残された唯一の選択肢は、例外ログの目的で最終的に使用するコードを記述するか、Greg Beech が示唆するように直接 IL を発行するルートを調べることです。

ただし、finally コードが実行されるかどうかは、使用しているデバッガーによって異なります。特に、VS は、finally が実行される前に未処理の例外で中断し、続行できなくなります。したがって、その時点でプロセスから切り離さない限り、ロギング コードは実行されません。つまり、2 番目の要件は、最初の要件を満たすことを妨げます。

于 2009-02-10T21:37:22.123 に答える
2

次のようなカスタム クラスにロジックをカプセル化できます。

    public  class Executor
{
    private readonly Action mainActionDelegate;
    private readonly Action onFaultDelegate;

    public Executor(Action mainAction, Action onFault)
    {
        mainActionDelegate = mainAction;
        onFaultDelegate = onFault;
    }

    public  void Run()
    {
        bool bad = true;
        try
        {
            mainActionDelegate();
            bad = false;
        }
        finally
        {
            if(bad)
            {
                onFaultDelegate();
            }
        }
    }

}

そしてそれを次のように使用します:

            new Executor(MightThrow, DoSomeLoggingOnFailure).Run();

お役に立てれば。

于 2009-02-11T00:37:06.247 に答える
1

これは次と同じではありませんか:

try 
{
    MightThrow();
}
catch (Exception e) 
{
    DoSomethingOnFailure();
    throw e;
}

?

于 2009-02-10T20:13:30.983 に答える
1

VB.net で、4 つのデリゲートを受け入れる TryFaultCatchFinally(of T) メソッドを実装する小さなアセンブリを作成するか、誰かに作成してもらうことができます。

  1. TryMethod -- 「Try」ブロックを実行する Action(of T)。
  2. FaultMethod -- Predicate(Of T, Exception) は、例外が発生した場合、「最終的に」ブロックが実行される前に呼び出されます。true を返す場合、Catch ブロックが実行されます。それ以外の場合は実行されません。
  3. CatchMethod -- 例外が発生し、FaultMethod が true を返した場合に実行される Action(Of T, Exception)。「最後に」ブロックが実行された後に発生します。
  4. FinalMethod -- 「Finally」ブロックとして実行される Action(OF T、Exception、Boolean)。TryMethod が完了するまで実行された場合、渡された例外は null になるか、終了の原因となった例外を保持します。ブール値は、例外がキャッチされた場合は true になり、それ以外の場合は false になります。

FaultMethod が実行されると、Finally ブロックによってそのような状態が破棄される前に、例外の原因となったオブジェクトの状態を調べることができる場合があることに注意してください。これを行うときは注意が必要ですが (例外がスローされたときに保持されていたロックは引き続き保持されます)、特にデバッグ時には、この機能が便利な場合があります。

ルーチンは次のようになることをお勧めします。

    Shared Sub TryFaultCatchFinally(Of T)(ByVal TryProc As Action(Of T), _
                                          ByVal FaultProc As Func(Of T, Exception, Boolean), _
                                          ByVal CatchProc As Action(Of T, Exception), _
                                          ByVal finallyProc As Action(Of T, Exception, Boolean), _
                                          T としての ByVal 値)
        theException を例外として薄暗くする = Nothing
        Dim exceptionCaught As Boolean = False
        試す
            TryProc(値)
            theException = なし
            exceptionCaught = False
        CopyExceptionAndReturnFalse(Ex, theException) OrElse FaultProc(Value, Ex) の場合に Ex を例外としてキャッチ
            exceptionCaught = True
            CatchProc(値、例)
        ついに
            FinalProc(Value, theException, exceptionCaught)
        エンドトライ
    サブ終了
于 2011-01-06T23:06:39.423 に答える
0

いいえ、これはあなたが持っている一般的なイディオムだと思います。

編集明確にするために、「キャッチ」してから「再スロー」戦略は同じランタイムセマンティクスを提供しますが、VSデバッガーが接続されているときのエクスペリエンスを変更します。ツールとメンテナンスは重要です。デバッグでは、多くの場合、「最初の例外をすべてキャッチする」必要があり、コードでキャッチしてから再スローするために多くの「偽の」最初の例外が発生する場合は、コードをデバッグする能力が本当に損なわれます。このイディオムは、ツールとうまくやり取りすること、および意図を明確に表現することに関するものです (「キャッチ」したくない、処理できないと判断して再スローするのではなく、例外が発生したことをログに記録したいだけですが、通過します)。

于 2009-02-10T20:12:42.857 に答える
0

DebuggerStepThrough 属性の使用を検討しましたか? http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerstepthroughattribute.aspx

[DebuggerStepThrough]
internal void MyHelper(Action someCallback)
{
    try
    {
        someCallback();
    }
    catch(Exception ex)
    {
        // Debugger will not break here
        // because of the DebuggerStepThrough attribute
        DoSomething(ex);
        throw;
    }
}
于 2011-01-13T15:00:14.357 に答える
0

C# 6 で追加された例外フィルターでは、次のように false を返す例外フィルターを使用することが 1 つのオプションです。

void PerformMightThrowWithExceptionLogging()
{
    try
    {
        MightThrow();
    }
    catch (Exception e) when (Log(e))
    {
        // Cannot enter here, since Log returns false.
    }
}

bool Log(Exception e)
{
   DoSomeLoggingOnFailure(e);
   // Return false so the exception filter is not matched, and therefore the stack is kept.
   // This means the debugger breaks where the exception actually happened, etc.
   return false;
}

例外フィルターの詳細については、https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catchを参照してください。

于 2020-12-04T20:07:49.233 に答える
-1
try
{
   MightThrow();
}
catch
{
   DoSomethingOnFailure();
}
于 2009-02-10T20:13:09.470 に答える