throwとthrow new Exception()の違い


164

違いは何ですか

try { ... }
catch{ throw } 

そして

try{ ... }
catch(Exception e) {throw new Exception(e.message) } 

2番目がメッセージを示しているかどうかに関係なく?


51
2番目のスニペットは、私が今まで見た中で最も邪悪な(しかし無害な)コード行の1つです。
SLaks

回答:


259

throw; 元の例外を再スローし、元のスタックトレースを保持します。

throw ex;元の例外をスローしますが、スタックトレースをリセットし、catchブロックまでのすべてのスタックトレース情報を破棄します。


決して書かないthrow ex;


throw new Exception(ex.Message);さらに悪い。新しいExceptionインスタンスが作成され、例外の元のスタックトレースとそのタイプが失われます。(例:)IOException
さらに、一部の例外には追加情報が含まれます(例:)ArgumentException.ParamName
throw new Exception(ex.Message); この情報も破壊されます。

場合によっては、すべての例外をカスタム例外オブジェクトにラップして、例外がスローされたときにコードが実行していたことに関する追加情報を提供することができます。

これを行うには、継承はという新しいクラスを定義しExceptionすべての4つの例外コンストラクタを追加し、必要に応じて取る追加コンストラクタInnerExceptionだけでなく、付加的な情報を、そして、新しい例外クラスをスロー渡すexInnerException、パラメータ。元のを渡すと、InnerExceptionスタックトレースを含む元の例外のプロパティがすべて保持されます。


24
「新しいException(ex);をスローするのはさらに悪い。」:私はこれに同意しません。例外のタイプを変更したい場合は、元の例外を内部例外として保持することが最善の方法です。throw new MyCustomException(myMessage, ex);もちろんあるはずですが。
Dirk Vollmar

9
0xA3の@:私は意味しex.Messageている、であるより悪いです。
SLaks

6
標準コンストラクタの実装に加えて、カスタム例外を作成する必要もあり[Serializable()]ます。
Dirk Vollmar、2010年

21
あなたが捕まえている間に捕まえることができるように、私たちはあなたが例外を好きになるように群がっているので、あなたが例外に例外を置きます。
ダース大陸

2
@SLaks:throw;例外が発生した実際の行番号がの行番号に置き換えられthrow;ます。それをどのように扱うことをお勧めしますか? stackoverflow.com/questions/2493779/...
エリック・J.

34

1つ目は、元のスタックトレースを保持します。

try { ... }
catch
{
    // Do something.
    throw;
}

2番目の方法では、例外のタイプやメッセージ、その他のデータを変更できます。

try { ... } catch (Exception e)
{
    throw new BarException("Something broke!");
}

内部例外を渡す3番目の方法もあります。

try { ... }
catch (FooException e) {
    throw new BarException("foo", e);
} 

私は以下を使用することをお勧めします:

  • 1つ目は、情報を破壊したり、エラーに関する情報を追加したりせずに、エラー状況でクリーンアップを実行する場合です。
  • エラーに関する詳細情報を追加する場合は3番目。
  • (信頼できないユーザーからの)情報を非表示にする場合の2番目。

6

他の人が見たことがないもう1つのポイント:

catch {}ブロックで何もしない場合、try ... catchを実行しても意味がありません。私はいつもこれを見ています:

try 
{
  //Code here
}
catch
{
    throw;
}

またはさらに悪い:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw ex;
}

最悪:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw new System.Exception(ex.Message);
}

最終条項がない限り、同意します。
トニロスマン2017

1
@ToniRossmannこの場合、スロー以外のことを行っていない限り、最後にキャッチなしでtry..finallyを使用します。
JLWarlow 2018

4

throwスタックトレースを保持しながら、キャッチされた例外を再スローしますthrow new Exceptionが、キャッチされた例外の詳細の一部は失われます。

通常はthrow、それ自体を使用して、その時点で完全に処理せずに例外をログに記録します。

BlackWaspには、C#での例外のスローというタイトルの十分な記事があります。


4

新しい例外をスローすると、現在のスタックトレースが吹き飛ばされます。

throw;元のスタックトレースを保持し、ほとんどの場合より便利です。そのルールの例外は、独自のカスタム例外に例外をラップする場合です。次に行う必要があります:

catch(Exception e)
{
    throw new CustomException(customMessage, e);
}

3

throwキャッチされた例外を再スローするためのものです。これは、呼び出しチェーンに渡す前に例外を使用して何かを実行したい場合に役立ちます。

throw引数なしで使用すると、デバッグのために呼び出しスタックが保持されます。


0

必要な場合は、元の例外を内部例外として設定して、新しい例外をスローできます。


0

2番目の例では、例外のスタックトレースをリセットします。1つ目は、例外の原因を最も正確に保持します。また、実際に何がうまくいかなかったかを知る上で重要な元のタイプのラップを解除しました...機能に2番目が必要な場合-たとえば、拡張情報を追加したり、カスタムの「HandleableException」などの特別なタイプで再ラップしたりすると、 InnerExceptionプロパティも設定されていることを確認してください。


ええ、これはあなたが速く書く必要がある質問の一つです。;)
Robert Harvey

0

最も重要な違いは、2番目の式が例外のタイプを消去することです。また、例外タイプは例外の捕捉に重要な役割を果たします。

public void MyMethod ()
{
    // both can throw IOException
    try { foo(); } catch { throw; }
    try { bar(); } catch(E) {throw new Exception(E.message); }
}

(...)

try {
    MyMethod ();
} catch (IOException ex) {
    Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
    Console.WriteLine ("Other error");    // [2]
}

foo()throwsの場合IOException[1]catchブロックは例外をキャッチします。しかしbar()投げるときIOException、それは普通のExceptionアリに変換され、[1]catchブロックによってキャッチされません。


0

throwまたはthrow ex。どちらも例外をスローまたは再スローするために使用されます。エラー情報をログに記録するだけで、呼び出し元に情報を送り返さない場合は、catch and leaveでエラーをログに記録するだけです。ただし、例外に関する意味のある情報を、throwまたはthrow exを使用する呼び出し元に送信する場合は、これで、throwとthrow exの違いは、throwがスタックトレースとその他の情報を保持することですが、throw exは新しい例外オブジェクトを作成するため、元のスタックトレースが失われます。したがって、throwとthrow eを使用する必要があるのはいつですか。コールスタック情報をリセットするなど、例外を再スローしたい場合がまだいくつかあります。たとえば、メソッドがライブラリ内にあり、ライブラリの詳細を呼び出しコードから非表示にしたい場合、ライブラリ内のプライベートメソッドに関する情報をコールスタックに含める必要はありません。その場合、ライブラリのパブリックメソッドで例外をキャッチし、それらを再スローして、コールスタックがそれらのパブリックメソッドで始まるようにすることができます。


0

スロー; 元の例外を再スローし、例外タイプを保持します。

新しいexception();をスローします。元の例外タイプを再スローし、例外スタックトレースをリセットします

exをスローします。例外スタックトレースをリセットし、例外タイプをリセットする


-1

ここでの答えはどれも違いを示していません。これは違いを理解するのに苦労している人々に役立つ可能性があります。次のサンプルコードを検討してください。

using System;
using System.Collections.Generic;

namespace ExceptionDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         void fail()
         {
            (null as string).Trim();
         }

         void bareThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw;
            }
         }

         void rethrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw e;
            }
         }

         void innerThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw new Exception("outer", e);
            }
         }

         var cases = new Dictionary<string, Action>()
         {
            { "Bare Throw:", bareThrow },
            { "Rethrow", rethrow },
            { "Inner Throw", innerThrow }
         };

         foreach (var c in cases)
         {
            Console.WriteLine(c.Key);
            Console.WriteLine(new string('-', 40));
            try
            {
               c.Value();
            } catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
         }
      }
   }
}

次の出力が生成されます。

Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
   --- End of inner exception stack trace ---
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

前の回答で示したように、基本スローは、失敗した元のコード行(12行目)と、例外が発生したときに呼び出しスタックでアクティブな他の2つのポイント(19行目と64行目)の両方を明確に示しています。

再スローのケースの出力は、それが問題である理由を示しています。このように例外が再スローされると、例外には元のスタック情報が含まれません。throw e(行35)と最も外側の呼び出しスタックポイント(行64)のみが含まれていることに注意してください。この方法で例外をスローすると、問題の原因としてfail()メソッドを追跡することが難しくなります。

最後のケース(innerThrow)は最も複雑で、上記のどちらよりも多くの情報が含まれています。新しい例外をインスタンス化しているので、コンテキスト情報(ここでは「外部」メッセージですが、新しい例外の.Data辞書に追加することもできます)を追加する機会があり、すべての情報を元の状態で保持します例外(ヘルプリンク、データディクショナリなどを含む)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.