c# - 例外 戻り値




なぜC#で例外をキャッチして、元に戻すのですか? (11)

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

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

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のように)。

参考文献:


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

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


C#の前(C#6以前)はVBが行うCILの "フィルタリングされた例外"をサポートしていないので、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
}

あなたはexをスローしたくありません。これはコールスタックを失うためです。 「 例外処理 (MSDN)」を参照してください。

そして、はい、try ... catchは何も役に立ちません(呼び出しスタックを失うことを除いて - 実際には悪い - 何らかの理由でこの情報を公開したくない場合を除く)。


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

あなたはCatch io.FileNotFoundExeption exと言って、別のファイルパスやそのようなものを使うかもしれませんが、まだエラーを投げます。

Throw Ex代わりにThrow Ex実行すると、完全なスタックトレースを維持することができます。 Throw exは、スタックトレースをthrow文から再開します(私は意味があると思います)。


これをしないで、

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
}

コードの例では、実際には、例外を捕まえることに何の意味もありません。コールスタックが失われたため、実際には害されません。 。

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

あなたが投稿した例で例外をキャッチすることには、何の意味もありません。 そんなことしないで!


人々が言及していない点は、.NET言語は本当に適切な区別をしないが、例外が発生したときにアクション取るべきかどうか、そしてそれを解決するかどうかという問題は、実際には異なる質問である。 解決を望んでいない例外に基づいて行動を取るべきケースが多く、例外を解決するために必要なのはスタックをある時点まで巻き戻すことである - それ以上のアクションは必要ありません。

自分が扱うことができるものを「キャッチ」するだけの一般的な知恵のために、例外が発生したときにアクションを起こすべき多くのコードはそうではありません。 例えば、多くのコードはロックを獲得し、ガードされたオブジェクトを "一時的に"その不変量に​​違反する状態にしてからオブジェクトを正当な状態にし、他の誰かがオブジェクトを見る前にロックを解放します。 オブジェクトが危険に無効な状態になっている間に例外が発生した場合、一般的にはその状態のままオブジェクトをロックしてロックを解除します。 はるかに良いパターンは、オブジェクトが「危険な」状態にある間に発生する例外を持つことであり、明示的にロックを無効にするので、将来取得しようとするとすぐに失敗する可能性があります。 このようなパターンを一貫して使用することで、いわゆる「ポケモン」例外処理の安全性が大幅に向上します.IMHOは、適切な処置を講じなくても例外を許可するコードが主な理由でIMHOの評判が悪くなります。

ほとんどの.NET言語では、コードが例外に基づいてアクションを実行する唯一の方法は、例外を解決しないことがわかっていてもそれをcatchし、問題のアクションを実行してからthrowです。 try/finallyブロックでokフラグを使用することで、コードが例外がスローされることに気にしない場合の別の方法があります。 okフラグをブロックの前にfalseに設定し、ブロックが終了する前、およびブロック内のreturn前にtrueに設定します。 finallyokが設定されていないと、例外が発生しているはずです。 このようなアプローチは、 catch / throwよりも意味的に優れていますが、醜いものであり、維持管理が容易ではありません。


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


例外を再発行する正当な理由は、例外に情報を追加したい場合や、オリジナルの例外を独自のものにする場合です。

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);
  }
}

最初; 記事中のコードが悪いことをする方法。 throw ex例外は、例外の呼び出しスタックをこのthrow文がある点にリセットします。 例外が実際に作成された場所に関する情報を失います。

第二に、もしあなたがちょうどそれを捕まえて再投げれば、私は付加価値はないと思っています。上記のコード例は、try-catchを使わない場合とまったく同じです。

ただし、例外をキャッチして再発行したい場合があります。 ロギングには次のようなものがあります。

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}

私の主な理由は次のようなコードです:

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

私はキャッチでブレークポイントを持つことができるので、それはインスタンス化された例外オブジェクトを持っています。 私は開発/デバッグ中にこれを多くしています。 もちろん、コンパイラは使用していないすべてのeを警告し、リリースビルド前に削除するのが理想的です。

彼らはしかし、デバッグ中にいいです。





try-catch