これら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.
}
}
Main
でtry & catch
が使用されていた場合は、throw;
を使用してエラーを再スローします。しかし、上記の単純化されたコードでは、すべての例外はHandleException
を通過します。
throw
内で呼び出された場合、throw ex;
はHandleException
を呼び出すのと同じ効果がありますか?
はい、違いがあります。
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;
}
}
(私は以前に投稿しました、そして@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をデバッグモードでのみ維持しますか?
他の答えは完全に正しいです、しかしこの答えは若干の余分な詳細を提供します、と思います。
この例を考えてください。
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
メンバーを変更するかもしれません)。
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です。行番号とともに
第二に..投げexで理解することができます。 M2メソッドのcatchブロックでthrowをthrow exに置き換えるだけです。以下のように。
throw exコードの出力は以下の通りです。
あなたは出力の違いを見ることができます.. throw exはちょうど前の階層をすべて無視し、throw exが書かれている行/メソッドでスタックトレースをリセットします。
いいえ、これにより例外のスタックトレースが異なります。 throw
ハンドラーで例外オブジェクトなしでcatch
を使用するだけで、スタックトレースは変更されません。
例外が再スローされるかどうかにかかわらず、HandleExceptionからブール値を返すことができます。
あなたがexを投げるとき、投げられるその例外は「オリジナルの」ものになります。そのため、以前のスタックトレースはすべてそこにはありません。
あなたがスローした場合、例外は行を下に行くだけで、あなたはフルスタックトレースを取得します。
MSDNの略:
一旦例外が投げられると、それが運ぶ情報の一部はスタックトレースです。スタックトレースは、例外をスローするメソッドで始まり、例外をキャッチするメソッドで終わるメソッド呼び出し階層のリストです。 throwステートメントで例外を指定して例外が再スローされると、スタックトレースは現在のメソッドで再開され、例外をスローした元のメソッドと現在のメソッドの間のメソッド呼び出しのリストは失われます。元のスタックトレース情報を例外とともに保持するには、例外を指定せずにthrowステートメントを使用します。
ここで見てください: 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);
これについて別の見方をすると、クライアントに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
}
}