「throw」と「throw ex」に違いはありますか?


437

これらの2つの違いはすでに何であるかを尋ねるいくつかの投稿があります。
(なぜ私はこれについても言及しなければならないのですか...)

しかし、私の質問は、別のエラーの神のような処理方法で「throw ex」と呼んでいる点で異なります。

public class Program {
    public static void Main(string[] args) {
        try {
            // something
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

try & catch使用された場合、エラーを再スローMainするために使用throw;します。しかし、上記のコードでは、すべての例外が通過しますHandleException

DOESはthrow ex;呼び出したのと同じ効果があるthrowの内側呼び出されたときにHandleException


3
違いがあります。それはスタックトレースが例外に表示されるかどうか、またはどのように表示されるかに関係しますが、どちらが現在どちらであるかを覚えていないので、これについては答えません。
Joel Coehoorn、2009

@ジョエル:ありがとう。HandleError例外の使用は悪い考えだと思います。エラー処理コードをリファクタリングしたかっただけです。
dance2die 2009

1
3番目の方法は、新しい例外をラップしてtimwise.blogspot.co.uk/2014/05/…
Tim Abell

回答:


679

はい、違いがあります。

  • throw exスタックトレースをリセットします(エラーはから発生したように見えますHandleException
  • throw しない-元の犯罪者は保持されます。

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }

28
マルクの答えを少し詳しく説明するには、ここで詳細を見つけることができます:geekswithblogs.net/sdorman/archive/2007/08/20/…–
スコットドーマン

3
@Shaul; いいえ、そうではありません。私はあなたの投稿へのコメントで詳細を与えました。
Marc Gravell

1
@Marc Gravell-お詫び、あなたは正しかった。反対票について申し訳ありません。元に戻すには遅すぎます... :(
Shaul Behr

3
@Marc:最初の例外がスローされたメソッドにスローが含まれていない場合にのみ、throwが元の違反者を保持しているようです(この質問を参照してください:stackoverflow.com/questions/5152265/…
Brann

3
@ScottDormanブログの移行後、リンクが正しく転送されていないようです。今ここにあるようです編集:ねえ、待って、それはあなたのブログです!自分のリンクを修正してください!; ^ D
ルフィン

96

(私は以前に投稿し、@ Marc Gravellが修正しました)

ここに違いのデモンストレーションがあります:

static void Main(string[] args) {
    try {
        ThrowException1(); // line 19
    } catch (Exception x) {
        Console.WriteLine("Exception 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // line 25
    } catch (Exception x) {
        Console.WriteLine("Exception 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // line 34
    } catch {
        throw; // line 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // line 41
    } catch (Exception ex) {
        throw ex; // line 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // line 49
}

そしてここに出力があります:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

例外1ではスタックトレースがDivByZero()メソッドに戻るのに対し、例外2ではスタックトレースが戻りません。

ただし、ThrowException1()およびに示されてThrowException2()いる行番号は、throwステートメントの行番号であり、への呼び出しの行番号ではないことに注意してくださいDivByZero()

リリースモードでの出力

例外1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

例外2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

元のstackTraceをデバッグモードでのみ維持しますか?


1
これは、コンパイラの最適化プロセスがなどの短いメソッドをインライン化するDevideByZeroため、スタックトレースは同じです。多分あなたはそれ自身の質問としてこれを投稿するべきです
Menahem

42

他の答えは完全に正しいですが、この答えはいくつかの余分な詳細を提供すると思います。

この例を考えてみましょう:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // line 34
      Div(a, b);   // line 35
      Mult(b, a);  // line 36
      Div(b, a);   // line 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

このthrow arithExc;行のコメントを外すと、出力は次のようになります。

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

確かに、その例外が発生した場所に関する情報を失っています。代わりにthrow;行を使用すると、次のようになります。

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

これは、Program.Div問題の原因となった方法であることがわかります。しかし、この問題がtryブロックの35行目または37行目にあるかどうかを確認することは依然として困難です。

3番目の選択肢を使用する場合、外側の例外でラップしても、情報は失われません。

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

特に、問題につながるのは35行目です。ただし、これを行うにはを検索する必要がInnerExceptionあり、単純なケースで内部例外を使用することは多少間接的に感じられます。

、このブログの記事彼らは(反射による)を呼び出すことで、行番号(tryブロックのラインを)維持internalintance法InternalPreserveStackTrace()上のExceptionオブジェクト。しかし、そのようなリフレクションを使用するのは良いことではありません(.NET Frameworkはinternalいつの日か警告なしにメンバーを変更する可能性があります)。


6

throwとthrow exの違いを理解しましょう。多くの.netインタビューで、このよくある質問が尋ねられていると聞きました。

これら2つの用語の概要を説明するために、throwとthrow exはどちらも、例外が発生した場所を理解するために使用されます。Throw exは、実際にどこでスローされたかに関係なく、例外のスタックトレースを書き換えます。

例で理解しましょう。

最初のスローを理解しましょう。

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

上記の出力は以下のとおりです。

は、実際に例外がスローされた完全な階層とメソッド名を示しています。これはM2-> M2です。行番号とともに

ここに画像の説明を入力してください

第二に、スローexで理解しましょう。M2メソッドのcatchブロックで、throwをthrow exに置き換えるだけです。以下のように。

ここに画像の説明を入力してください

throw exコードの出力は次のとおりです。

ここに画像の説明を入力してください

出力の違いを確認できます。throwexは、以前の階層をすべて無視し、throw exが書き込まれた行/メソッドでスタックトレースをリセットします。


5

実行するとthrow ex、そのスローされた例外が「元の」例外になります。したがって、以前のすべてのスタックトレースは存在しません。

実行するthrowと、例外が発生、完全なスタックトレースが取得されます。


4

いいえ、これにより例外のスタックトレースが異なります。ハンドラーでthrow例外オブジェクトなしでのみ使用するcatchと、スタックトレースは変更されません。

例外が再スローされるかどうかに関係なく、HandleExceptionからブール値を返すことができます。


4

MSDNの略

例外がスローされると、その情報の一部がスタックトレースになります。スタックトレースは、例外をスローするメソッドで始まり、例外をキャッチするメソッドで終わるメソッド呼び出し階層のリストです。throwステートメントで例外を指定して例外を再スローすると、スタックトレースが現在のメソッドで再開され、例外をスローした元のメソッドと現在のメソッド間のメソッド呼び出しのリストが失われます。例外のある元のスタックトレース情報を保持するには、例外を指定せずにthrowステートメントを使用します。


2

ここを見てください:http : //blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

投げる

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

例外のあるスタック情報を保持します

これは「再スロー」と呼ばれます

新しい例外をスローしたい場合は、

throw new ApplicationException("operation failed!");

スローEx

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

それは例外でスタック情報を送信しません

これは「スタックを壊す」と呼ばれます

新しい例外をスローしたい場合は、

throw new ApplicationException("operation failed!",ex);

0

これについて別の見方をすると、クライアントにAPIを提供していて、内部ライブラリの詳細なスタックトレース情報を提供したい場合は、throwの使用が特に役立ちます。ここでスローを使用することにより、File.DeleteのSystem.IO.Fileライブラリの場合、スタックトレースを取得します。throw exを使用すると、その情報はハンドラーに渡されません。

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}

-1
int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. 1、2、3のすべての行がコメント化されている場合-出力-内部ex

  2. すべての2行目と3行目がコメント化されている場合-出力-内部ex System.DevideByZeroException:{"ゼロで除算しようとしました。"} ---------

  3. すべての行1と2がコメント化されている場合-出力-内部ex System.Exception:devide by 0 ----

  4. すべての行1および3がコメント化されている場合-出力-内部ex System.DevideByZeroException:{"ゼロで除算しようとしました。"} ---------

そして、StackTraceはthrow exの場合にリセットされます。

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