web-dev-qa-db-ja.com

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

これら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.
    }
}

Maintry & catchが使用されていた場合は、throw;を使用してエラーを再スローします。しかし、上記の単純化されたコードでは、すべての例外はHandleExceptionを通過します。

throw内で呼び出された場合、throw ex;HandleExceptionを呼び出すのと同じ効果がありますか?

388
Sung M. Kim

はい、違いがあります。

  • 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;
        }
    }
    
620
Marc Gravell

(私は以前に投稿しました、そして@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ステートメントの行番号です。notDivByZero()への呼び出しの行番号です。ちょっと...

リリースモードで出力

例外1:

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

例外2:

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

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

89
Shaul Behr

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

この例を考えてください。

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

特に問題があるのは5行目です。しかし、これはInnerExceptionを検索することを人々に要求し、単純な場合に内部例外を使用することはいくぶん間接的に感じます。

このブログ記事 彼らは(反射を通して)internalオブジェクトのException intanceメソッドInternalPreserveStackTrace()を呼び出すことによって行番号(tryブロックの行)を保存します。しかし、そのようなリフレクションを使うのはいいことではありません(.NET Frameworkはいつか警告なしにinternalメンバーを変更するかもしれません)。

37

throwとthrow exの違いを理解しましょう。私は多くの.netインタビューでこの一般的な質問がされていると聞きました。

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

例で理解しましょう。

最初のThrowを理解しましょう。

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です。行番号とともに

enter image description here

第二に..投げexで理解することができます。 M2メソッドのcatchブロックでthrowをthrow exに置き換えるだけです。以下のように。

enter image description here

throw exコードの出力は以下の通りです。

enter image description here

あなたは出力の違いを見ることができます.. throw exはちょうど前の階層をすべて無視し、throw exが書かれている行/メソッドでスタックトレースをリセットします。

5
Mahesh

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

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

4
Lucero

あなたがexを投げるとき、投げられるその例外は「オリジナルの」ものになります。そのため、以前のスタックトレースはすべてそこにはありません。

あなたがスローした場合、例外は行を下に行くだけで、あなたはフルスタックトレースを取得します。

4
GR7

MSDNの略

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

3
A.S.

ここで見てください: 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);
1
Aaaaaaaa

これについて別の見方をすると、クライアントにAPIを提供していて、内部ライブラリの冗長スタックトレース情報を提供したい場合は、throwを使用することが特に便利です。ここで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
    }
}
0
Charles Owen