641

私は記事C# -シリアライズ可能な DTO 上のデータ転送オブジェクトを見ています。

この記事には、次のコードが含まれています。

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

記事の残りの部分は (初心者にとっては) 正気で合理的に見えますが、その try-catch-throw は WtfException をスローします...これは、例外をまったく処理しないこととまったく同じではありませんか?

エルゴ:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

または、C# でのエラー処理に関する基本的な何かが欠けていますか? Java とほぼ同じです (チェック例外を除いて) ですね。... つまり、どちらも C++ を改良したものです。

スタック オーバーフローの質問パラメータなしのキャッチを再スローすることと何もしないことの違いは? try-catch-throw はノーオペレーションであるという私の主張を支持しているようです。


編集:

将来このスレッドを見つけた人のために要約すると...

しない

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

スタック トレース情報は、問題の根本原因を特定する上で非常に重要です。

行う

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

より具体的な例外よりも具体的な例外をキャッチします (Java と同様)。


参考文献:

4

17 に答える 17

486

初め; 記事のコードのやり方は悪です。throw ex例外の呼び出しスタックを、このthrowステートメントがあるポイントにリセットします。例外が実際に作成された場所に関する情報が失われます。

第二に、そのようにキャッチして再スローした場合、付加価値は見られません。上記のコード例はthrow ex、try-catchがなくても同じくらい良い(または、少し言えばさらに良い)でしょう。

ただし、例外をキャッチして再スローしたい場合があります。ロギングはそれらの1つである可能性があります。

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}
于 2009-05-19T08:02:45.000 に答える
127

これをしないでください、

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

スタックトレース情報が失われます...

どちらか、

try { ... }
catch { throw; }

また

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

再スローする理由の1つは、たとえば、さまざまな例外を処理している場合です。

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}
于 2009-05-19T08:03:23.267 に答える
60

C# (C# 6 より前) は CIL の「フィルター処理された例外」をサポートしていません (VB はサポートしています)。そのため、C# 1-5 で例外を再スローする理由の 1 つは、catch() の時点で十分な情報がないことです。実際に例外をキャッチするかどうかを判断します。

たとえば、VBでは次のことができます

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

...これは、異なる ErrorCode 値を持つ MyExceptions を処理しません。v6 より前の C# では、ErrorCode が 123 でない場合、MyException をキャッチして再スローする必要がありました。

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

C# 6.0 以降、VB と同じようにフィルタリングできます。

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}
于 2009-05-19T08:20:21.640 に答える
18

次のようなコードを使用する主な理由:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

インスタンス化された例外オブジェクトを持つキャッチにブレークポイントを設定できるようにするためです。開発/デバッグ中にこれを頻繁に行います。もちろん、コンパイラは未使用のすべての e について警告を表示します。理想的には、リリース ビルドの前にそれらを削除する必要があります。

ただし、デバッグ中は便利です。

于 2009-09-18T09:50:33.167 に答える
13

例外を再スローする正当な理由は、例外に情報を追加したい場合、または元の例外を独自の作成の1つでラップしたい場合です。

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}
于 2009-05-19T08:03:22.300 に答える
10

これは、例外をまったく処理しないこととまったく同じではありませんか?

正確ではありません。同じではありません。例外のスタック トレースをリセットします。これはおそらく間違いであり、悪いコードの例であることに同意しますが。

于 2009-05-19T08:11:11.770 に答える
8

exをスローしたくない-これはコールスタックを失うからです。例外処理(MSDN)を参照してください。

そして、はい、try ... catchは何の役にも立ちません(コールスタックを失うことを除けば、実際にはもっと悪いですが、何らかの理由でこの情報を公開したくない場合を除きます)。

于 2009-05-19T08:02:32.690 に答える
5

人々が言及していない点は、.NET 言語は実際には適切な区別をしていませんが、例外が発生したときにアクションを実行する必要があるかどうか、およびそれを解決するかどうかという問題は、実際には別個の問題であるということです。解決する見込みのない例外に基づいてアクションを実行する必要がある場合が多くあります。また、例外を「解決」するために必要なのはスタックを特定のポイントまで巻き戻すことだけである場合もあります。これ以上のアクションは必要ありません。 .

「処理」できるものだけを「キャッチ」する必要があるという一般的な知恵のために、例外が発生したときにアクションを実行する必要がある多くのコードが実行されません。たとえば、多くのコードはロックを取得し、保護されたオブジェクトを「一時的に」その不変条件に違反する状態にし、次にそのオブジェクトを正当な状態にし、他の誰かがオブジェクトを見る前にロックを解除します。オブジェクトが危険なほど無効な状態にあるときに例外が発生した場合、一般的な方法は、オブジェクトがまだその状態にある状態でロックを解放することです。はるかに優れたパターンは、オブジェクトが「危険な」状態にある間に発生する例外を明示的にロックを無効にすることです。これにより、今後それを取得しようとするとすぐに失敗します。

ほとんどの .NET 言語では、コードが例外に基づいてアクションを実行する唯一の方法はcatch(例外を解決しないことがわかっていても)、問題のアクションを実行してから再実行することthrowです)。コードがどの例外がスローされるかを気にしない場合に考えられる別のアプローチは、ブロックでokフラグを使用することです。フラグをブロックの前、ブロックが終了する前、およびブロック内にあるものの前にtry/finally設定します。次に、 内で、が設定されていない場合は、例外が発生したに違いないと想定します。このようなアプローチは意味的には/よりも優れていますが、醜く、必要以上に保守しにくくなります。okfalsetruereturnfinallyokcatchthrow

于 2013-10-14T16:56:17.780 に答える
4

While many of the other answers provide good examples of why you might want to catch an rethrow an exception, no one seems to have mentioned a 'finally' scenario.

An example of this is where you have a method in which you set the cursor (for example to a wait cursor), the method has several exit points (e.g. if () return;) and you want to ensure the cursor is reset at the end of the method.

To do this you can wrap all of the code in a try/catch/finally. In the finally set the cursor back to the right cursor. So that you don't bury any valid exceptions, rethrow it in the catch.

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}
于 2016-08-09T11:47:08.870 に答える
3

キャッチスローの考えられる理由の1つは、スタックのより深いところにある例外フィルターがフィルターダウンされないようにすることです(ランダムな古いリンク)。しかしもちろん、それが意図されていれば、そこにそう言っているコメントがあるでしょう。

于 2009-07-30T15:12:20.113 に答える
3

これは、catchブロックで何をしているか、およびエラーを呼び出し元のコードに渡したいかどうかによって異なります。

と言っCatch io.FileNotFoundExeption exてから別のファイルパスなどを使用しても、エラーが発生します。

また、Throw代わりにThrow Ex実行すると、完全なスタックトレースを保持できます。Throw exは、throwステートメントからスタックトレースを再開します(それが理にかなっていることを願っています)。

于 2009-05-19T08:04:03.110 に答える
2

あなたが投稿したコードの例では、実際には、キャッチに対して何も行われていないため、例外をキャッチしても意味がありません。実際には、コールスタックが失われるため、良いよりも害があります。 .

ただし、例外が発生した場合に何らかのロジックを実行するために例外をキャッチし (たとえば、ファイル ロックの SQL 接続を閉じたり、ログを記録したり)、呼び出し元のコードにスローして処理します。これは、フロント エンド コードよりもビジネス レイヤーでより一般的です。これは、ビジネス レイヤーを実装するコーダーに例外を処理させたい場合があるためです。

繰り返しますが、投稿した例で例外をキャッチしても意味がありません。そんなことしないで!

于 2009-05-19T08:19:11.677 に答える
1

申し訳ありませんが、「改良されたデザイン」などの多くの例は、依然としてひどいにおいがするか、非常に誤解を招く可能性があります。{} catch {log; }を投げるのはまったく無意味です。例外ログは、アプリケーション内の中央の場所で実行する必要があります。とにかく例外がスタックトレースをバブルアップします。システムの境界の近くのどこかにそれらを記録してみませんか?

コンテキスト(つまり、1つの例ではDTO)をログメッセージにシリアル化する場合は注意が必要です。ログファイルにアクセスできるすべての人の手に渡りたくない機密情報を簡単に含めることができます。また、例外に新しい情報を追加しない場合、例外の折り返しのポイントは実際にはわかりません。古き良きJavaにはそのためのポイントがあります。呼び出し元は、コードを呼び出すときに予想される例外の種類を知っている必要があります。.NETにはこれがないので、私が見たケースの少なくとも80%では、ラッピングは何の役にも立ちません。

于 2009-09-16T07:22:00.817 に答える
1

他の人が言ったことに加えて、関連する質問への私の回答を参照してください。これは、キャッチと再スローがノーオペレーションではないことを示しています (VB にありますが、一部のコードは VB から C# で呼び出すことができます)。

于 2009-09-22T19:53:01.980 に答える
1

シナリオ catch-log-rethrow について話している回答のほとんど。

コードに記述する代わりに、AOP を使用することを検討してください。

于 2012-12-25T22:41:10.177 に答える