try - throw new exception c#




한 번에 여러 예외를 잡으시겠습니까? (18)

단순히 System.Exception catch하는 것은 바람직하지 않습니다. 대신 "알려진"예외 만 잡아야합니다.

이제 이것은 때때로 불필요한 반복 코드로 이어집니다. 예를 들면 다음과 같습니다.

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

나는 두 가지 예외를 모두 잡아서 WebId = Guid.Empty 호출 만 호출하는 방법이 있을까요?

주어진 예제는 단지 GUID 이기 때문에 다소 간단합니다. 그러나 객체를 여러 번 수정하는 코드를 상상해보십시오. 조작 중 하나가 예상대로 실패하면 object 를 "재설정"하려고 object . 그러나 예기치 않은 예외가있는 경우에도 여전히 예외를 던지고 싶습니다.


주의와 경고 : 또 다른 종류, 기능적 스타일.

링크에 포함 된 내용은 직접 질문에 답변하지 않지만 다음과 같이 확장하는 것은 쉽지 않습니다.

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(기본적으로 자신을 반환하는 또 다른 빈 Catch 오버로드를 제공하십시오)

이것에 대한 더 큰 의문이있는 이유 입니다. 나는 비용이 여기에 이득보다 중요하다고 생각하지 않습니다 :)


@Mheheal

약간 수정 된 코드 버전 :

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

문자열 비교는 추악하고 느립니다.


C # 7을 사용하면 switch 문을 쉽게 읽을 수 있도록 유지하면서 Michael Stum의 대답을 향상시킬 수 있습니다.

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

C #에서는 불행히도 예외 필터가 필요하므로 C #은 MSIL의 해당 기능을 노출하지 않습니다. VB.NET에는이 기능이 있지만, 예를 들어

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

당신이 할 수있는 일은 익명의 함수를 사용하여 오류 코드를 캡슐화 한 다음 해당 특정 catch 블록에서 호출하는 것입니다.

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

다른 사람들이 지적했듯이 catch 블록 내에 if 문을 사용하여 어떤 일이 일어나고 있는지 확인할 수 있습니다. C # 6은 예외 필터를 지원하므로 다음과 같이 작동합니다.

try { … }
catch (Exception e) when (MyFilter(e))
{
    …
}

MyFilter 메서드는 다음과 같이 보일 수 있습니다.

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

또는, 이것은 모두 인라인으로 처리 할 수 ​​있습니다 (when 문 오른쪽은 부울 표현식이어야합니다).

try { … }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    …
}

이것은 catch 블록 내에서 if 문을 사용하는 것과는 다르며 예외 필터 사용하면 스택을 unwind 하지 않습니다 .

Visual Studio 2015 를 다운로드하여 확인할 수 있습니다.

Visual Studio 2013을 계속 사용하려면 다음과 같은 uget 패키지를 설치할 수 있습니다.

Microsoft.Net.Compilers 설치 패키지

이 글을 쓰는 시점에서 C # 6에 대한 지원이 포함됩니다.

이 패키지를 참조하면 시스템에 설치된 버전과 달리 패키지에 포함 된 특정 버전의 C # 및 Visual Basic 컴파일러를 사용하여 프로젝트를 빌드 할 수 있습니다.


어때?

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}

응용 프로그램을 C # 6으로 업그레이드 할 수 있다면 운이 좋다. 새로운 C # 버전에서는 예외 필터가 구현되었습니다. 그래서 다음과 같이 작성할 수 있습니다.

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

어떤 사람들은이 코드가

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

그러나 그렇지 않습니다. 실제로 이것은 이전 버전에서 에뮬레이트 할 수없는 C # 6의 유일한 새로운 기능입니다. 첫째, 재 드로잉은 캐치를 건너 뛰는 것보다 더 많은 오버 헤드를 의미합니다. 둘째로, 의미 상 동일하지 않습니다. 새로운 기능은 코드를 디버깅 할 때 스택을 그대로 유지합니다. 이 기능이 없으면 크래시 덤프가 덜 유용하거나 쓸모가 없습니다.

roslyn.codeplex.com/discussions/541301 에서 이에 roslyn.codeplex.com/discussions/541301 참조하십시오. 그리고 그 차이를 보여주는 예 .


이것은 매트의 대답의 변형입니다 (나는 이것이 조금 더 깨끗하다고 ​​느낍니다) ... 방법을 사용하십시오 :

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

다른 예외가 발생하고 코드 WebId = Guid.Empty; 맞지 않을거야. 다른 예외로 인해 프로그램이 중단되는 것을 원하지 않는 경우 다른 두 가지 캐치 후에 다음을 추가하십시오.

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

Joseph Daigle의 대답 은 좋은 해결책이지만 다음 구조가 조금 더 깔끔하고 오류가 발생하지 않는 것으로 나타났습니다.

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

표현식을 뒤집는 데는 몇 가지 장점이 있습니다.

  • return 문은 필요하지 않습니다.
  • 코드가 중첩되어 있지 않습니다.
  • Joseph의 솔루션에서 표현과 분리 된 'throw'또는 'return'진술을 잊을 위험이 없습니다.

심지어 단일 라인으로 압축 될 수 있습니다 (별로 좋지는 않지만).

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

편집 : C # 6.0의 예외 필터링 은 구문을 조금 더 깨끗하게 만들며 현재 솔루션보다 많은 다른 이점 을 제공합니다. (가장 주목할 만하게 더미는 무사히 떠난다)

다음은 C # 6.0 구문을 사용하여 동일한 문제가 나타나는 방식입니다.

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

System.Exception catch하고 유형을 켜십시오.

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

편집 : 나는 C # 6.0에서 예외 필터가 지금 완벽하게 좋은 방법이라고 말하고있는 다른 사람들과 동의합니다 : catch (Exception ex) when (ex is ... || ex is ... )

나는 아직도 한 줄짜리 레이아웃을 싫어하고 다음과 같이 개인적으로 코드를 배치 할 것을 제외하고는. 나는 그것이 이해력을 향상 시킨다는 것을 믿기 때문에 이것은 이것이 미학적 인 것처럼 기능적이라고 생각한다. 일부는 동의하지 않을 수 있습니다.

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

기발한:

여기 파티가 좀 늦었다는 거 알지만, 거룩한 연기는 ...

추적을 똑바로 자르면, 이런 종류의 이전 답변은 중복되지만, 여러 예외 유형에 대해 공통적 인 조치를 수행하고 한 가지 방법의 범위 내에서 모든 것을 깔끔하게 유지하려면 왜 람다를 사용하지 않는가? / closure / inline 함수를 사용하여 다음과 같은 작업을 수행 할 수 있습니까? 내 말은, 당신이 단지 그 클로저를 당신이 그 곳곳에서 활용할 수있는 분리 된 방법으로 만들고 싶다는 것을 깨닫게 될 확률이 꽤 높다는 것입니다. 그러나 나머지 코드는 구조적으로 변경하지 않고도 그렇게하기가 쉽습니다. 권리?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

나는 근본적으로 다음을 대체하기 위해이 지구상의 모든 노력에 왜 도움이되는지 궁금합니다 ( 경고 : 약간의 아이러니 / 풍자).

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

...이 다음 코드 냄새의 미친 변형과 함께, 나는 단지 몇 가지 키 스트로크를 저장하는 척하는 예를 의미합니다.

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

왜냐하면 그것은 확실히 자동으로 더 읽기 쉽지 않기 때문입니다.

허락 해, 나는 /* write to a log, whatever... */ return; 의 3 개의 동일한 인스턴스를 /* write to a log, whatever... */ return; 첫 번째 예에서

하지만 그게 내 요점이야. 모두 기능 / 방법에 대해 들어 봤어? 진지하게. 일반적인 ErrorHandler 함수를 작성하고, 각 catch 블록에서 호출하십시오.

여러분이 나에게 묻는다면, 두 번째 예제 ( ifis 키워드 사용)는 프로젝트의 유지 관리 단계에서 읽기 쉽지 않으며 동시에 오류 발생 가능성이 훨씬 큽니다.

상대적으로 프로그래밍에 익숙하지 않은 사용자를위한 유지 관리 단계는 프로젝트의 전체 수명 기간 중 98.7 % 이상을 차지하게 될 것이며 유지 관리를 수행하는 불량배는 거의 확실하게 다른 사람이 될 것입니다. 그리고 당신의 이름을 저주하는 직업에 그들의 시간의 50 %를 쓸 아주 좋은 기회가 있습니다.

그리고 당연히 FxCop이 당신을 짖으며 실행중인 프로그램과 정확하게 관련이있는 코드에 속성을 추가해야합니다. FxCop이 99.9 %의 사례에서 완전히 그렇다는 것을 무시하도록 FxCop에 지시합니다. 신고하는 것이 맞다. 그리고, 죄송 합니다만, 나는 틀릴 수도 있습니다. 그러나 "ignore"속성이 실제로 앱에 컴파일되는 것을 끝내지는 않습니까?

하나의 라인에 전체 if 테스트를두면 더 읽기 쉬울 것입니까? 나는 그렇게 생각하지 않는다. 나는 한 프로그래머가 한 줄에 더 많은 코드를 넣으면 실행 속도가 빨라질 것이라고 오래 전에 주장했다. 그러나 물론 그는 엄청난 열매를 맺고 있었다. 인터프리터 나 컴파일러가 긴 라인을 개별 라인 당 하나의 명령문으로 분리하는 방법에 대해서는 (직설적 인 얼굴로 - 도전적이었던) 설명했다. 컴파일러를 외면하려고 시도하는 대신 코드를 읽을 수있게 만들었습니다. 아무런 영향을 미치지 않았습니다. 그러나 나는 빗 나간다.

지금부터 1 ~ 2 개의 예외 유형을 추가 할 때 얼마나 쉽게 읽을 수 있습니까? (답 : 읽기 쉽지 않습니다.)

주요 요점 중 하나는 실제로 우리가 매일보고있는 텍스트 소스 코드를 포맷하는 대부분의 요점은 코드가 실행될 때 실제로 일어나고있는 일을 다른 사람에게 분명히 알리는 것입니다. 왜냐하면 컴파일러는 소스 코드를 완전히 다른 것으로 바꾸어 코드 서식 스타일을 신경 쓰지 않기 때문입니다. 그래서 all-on-one-line은 완전히 짜증납니다.

그냥 ...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

C # 6.0에서 Exception Filters는 예외 처리 개선입니다.

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}

어쩌면 catch 절 안에 있지 않은 코드의 다른 부분 에서처럼 일반 코드를 메서드에 넣는 등의 간단한 코드를 유지하려고 시도 할 수 있습니까?

예 :

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

내가 어떻게 할 것인가, 심플함 을 찾는 것은 아름다운 패턴이다.


이 답변이 표면에 닿았을 때부터 조금 더 깊이 파고 들려고했습니다.

그래서 우리가 정말로하고 싶은 것은 컴파일하지 않는 것입니다.

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

우리가 이것을 원하게되는 이유는 예외 처리기가 나중에 우리가 필요로하는 것들을 잡는 것을 원하지 않기 때문입니다. 물론, 우리는 Exception을 잡아서 'if'로 무엇을 할지를 검사 할 수 있습니다. 그러나 솔직히 말하면, 우리는 그것을 정말로 원하지 않습니다. (FxCop, 디버거 문제, 추한)

그렇다면 왜이 코드를 컴파일하지 않을 것인가? 그런 식으로 어떻게 해킹 할 수 있을까?

코드를 보면 우리가 정말로하고 싶은 것은 콜을 전달하는 것입니다. 그러나 MS 파티션 II에 따르면 일리노이 예외 핸들러 블록은 이와 같이 작동하지 않습니다.이 경우 '예외'개체가 다른 유형을 가질 수 있으므로이 경우 이해가됩니다.

또는 코드로 작성하기 위해 컴파일러에게 이와 같은 작업을 수행하도록 요청합니다 (완전히 정확하지는 않지만 아마도 가장 가까운 것입니다).

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

이것이 컴파일되지 않는 이유는 '$ 예외'객체가 가지고있는 유형과 값 (여기에 변수 'e'에 저장되어있는 값)은 무엇인가? 우리가 컴파일러에서 처리하도록하는 방법은 두 예외의 공통 기본 유형이 'Exception'이며, 두 예외를 모두 포함하는 변수를 사용하고 catch 된 두 예외 만 처리하는 것입니다. 이것이 일리노이에서 구현되는 방식은 '필터'로서 VB.Net에서 사용할 수 있습니다.

C #에서 작동하게하려면 올바른 'Exception'기본 유형을 가진 임시 변수가 필요합니다. 코드의 흐름을 제어하기 위해 일부 분기를 추가 할 수 있습니다. 여기에 간다 :

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

이것에 대한 명백한 단점은 우리가 제대로 다시 던질 수 없다는 것입니다. 그리고 정직하게 말하면 꽤 못생긴 해결책입니다. 추악함은 분기 제거를 수행함으로써 약간 수정 될 수 있으며, 이는 솔루션을 약간 더 좋게 만듭니다.

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

그것은 단지 '다시 던지기'를 떠난다. 이것이 작동하려면 'catch'블록 내부에서 처리를 수행 할 수 있어야합니다.이 작업을 수행하는 유일한 방법은 잡는 'Exception'객체입니다.

이 시점에서 과부하 해결을 사용하거나 예외를 처리하기 위해 여러 유형의 예외를 처리하는 별도의 함수를 추가 할 수 있습니다. 둘 다 단점이 있습니다. 시작하려면 헬퍼 함수로 수행하는 방법이 있습니다.

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

그리고 다른 해결책은 Exception 객체를 잡아서 그에 따라 처리하는 것입니다. 위의 컨텍스트에 기반하여이를위한 가장 직역적인 변환은 다음과 같습니다.

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

결론은 다음과 같습니다.

  • 우리가 다시 던지고 싶지 않다면, 우리는 적절한 예외를 잡아서 임시로 저장하는 것을 고려할 것입니다.
  • 핸들러가 단순하고 코드를 다시 사용하려면 최상의 솔루션은 도우미 기능을 도입하는 것입니다.
  • 우리가 다시 던지고 싶다면 FxCop과 디버거의 잡히지 않은 예외를 벗어나는 'Exception'catch 핸들러에 코드를 넣을 수 밖에 없습니다.

그래서 모든 예외 스위치 내에서 많은 코드를 반복하고 있습니까? 방법 추출과 같은 소리는 신 아이디어 일 텐데, 그렇지 않니?

따라서 코드가 아래와 같습니다.

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

왜 아무도 그 코드 중복을 알아 차렸는지 궁금합니다.

C # 6부터는 이미 다른 사람들이 언급 한 exception-filters 가 있습니다. 그래서 위의 코드를 다음과 같이 수정할 수 있습니다 :

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

내가 그것을하는 한 방법을 찾았지만, 이것은 The Daily WTF의 재료와 비슷해 보인다 .

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

이미 긴 스레드에 짧은 대답을 추가하고 싶었습니다. 언급되지 않은 무언가는 catch 문의 우선 순위입니다. 특히 잡으려고하는 각 예외 유형의 범위를 알고 있어야합니다.

예를 들어 Exception 으로 "catch-all"예외를 사용하면 다른 모든 catch 문보다 우선하며 컴파일러 오류가 발생하지만 순서를 바꾸면 catch 문을 연결할 수 있습니다. ) catch-all Exception 타입을 하단에 놓을 수 있습니다. 그러면 try..catch 블록에서 상한값을 맞추지 못했던 예외가 캡처됩니다.

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

나는이 MSDN 문서를 검토하는 것이 좋습니다.

예외 계층 구조


catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}




exception-handling