“throw”와“throw ex”에는 차이가 있습니까?


437

그 둘의 차이점이 이미 무엇인지 묻는 게시물이 있습니다.
(왜 이것을 언급해야합니까?)

그러나 내 질문은 다른 오류 신과 같은 처리 방법 에서 "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

합니까는 throw ex;호출하는 것과 같은 효과가 throw내부 전화했을 때를 HandleException?


3
차이점은 스택 추적이 예외에 나타나는지 여부와 관련이 있지만 지금은 어느 것이 기억 나지 않으므로이 답변을 나열하지 않습니다.
Joel Coehoorn

@Joel : 감사합니다. HandleError 예외를 사용하는 것은 나쁜 생각이라고 생각합니다. 방금 오류 처리 코드를 리팩터링하고 싶었습니다.
dance2die

1
세 번째 방법은 새로운 예외에 싸서 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
Marc의 답변을 조금 확장하려면 여기에서 자세한 내용을 찾을 수 있습니다. geekswithblogs.net/sdorman/archive/2007/08/20/…
Scott Dorman

3
@ 샤울; 아뇨. 귀하의 게시물에 댓글로 세부 사항을 제공했습니다.
Marc Gravell

1
@Marc Gravell-사과드립니다. 공감에 대해 죄송합니다. 실행 취소하기에는 너무 늦었습니다 ... :(
Shaul Behr

3
@Marc : 초기 예외가 발생한 방식으로 던져가 아닌 경우에만 throw가 원래의 가해자를 보존하는 것 같습니다 (이 질문 참조 : stackoverflow.com/questions/5152265/… )
Brann

3
@ScottDorman 블로그 이전 후 링크가 제대로 전달되지 않는 것 같습니다. 것 같은데 지금 여기 살고있다 . 편집 : 이봐, 당신의 블로그입니다! 자신의 링크를 수정하십시오! ; ^ D
ruffin

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

다른 대답은 전적으로 정확하지만이 대답은 약간의 추가 detalis를 제공한다고 생각합니다.

이 예제를 고려하십시오.

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 번째 줄에서 오는지 여전히 확인하기 어렵습니다 .

외부 예외로 래핑하는 세 번째 대안을 사용하면 정보가 손실되지 않습니다.

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

던지기와 던지기의 차이점을 이해합시다. 많은 .net 인터뷰 에서이 일반적인 질문을 받고 있다고 들었습니다.

이 두 용어에 대한 개요를 제공하기 위해 throw 및 throw ex는 예외가 발생한 위치를 이해하는 데 사용됩니다. 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입니다. 줄 번호와 함께

여기에 이미지 설명을 입력하십시오

둘째로 .. 던져 전으로 이해합니다. M2 메서드 catch 블록에서 throw를 throw ex로 ​​바꾸십시오. 아래.

여기에 이미지 설명을 입력하십시오

ex ex 코드의 출력은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

출력의 차이를 볼 수 있습니다. throw ex는 이전의 모든 계층 구조를 무시하고 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!");

예를 던져 :

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를 사용하는 것이 특히 유용합니다. 여기서 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 : { "0으로 나누려고했습니다."} ---------

  3. 모든 라인 1과 2가 주석 처리 된 경우-출력-내부 ex 시스템 예외 : 0으로 편차 ----

  4. 모든 라인 1과 3이 주석 처리 된 경우-출력-내부 ex System.DevideByZeroException : { "0으로 나누려고했습니다."} ---------

그리고 ex를 던질 경우 StackTrace가 재설정됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.