[c#] 한 번에 여러 예외를 잡으시겠습니까?

단순히 잡는 것은 바람직하지 않습니다 System.Exception. 대신 “알려진”예외 만 포착해야합니다.

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

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

궁금합니다 : 두 예외를 모두 잡아서 WebId = Guid.Empty한 번만 전화를 걸 수있는 방법이 있습니까?

주어진 예제는 단지이므로 간단합니다 GUID. 그러나 객체를 여러 번 수정하는 코드를 상상하고 조작 중 하나가 예상대로 실패하면을 “재설정”하려고합니다 object. 그러나 예기치 않은 예외가있는 경우 여전히 더 높이 던지고 싶습니다.



답변

캐치 System.Exception및 유형 켜기

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;첫 번째 예 에서 세 개의 동일한 인스턴스를 남겼습니다 .

그러나 그것은 내 요점입니다. 여러분은 기능 / 방법에 대해 들어 보았습니다. 진심으로. 공통 ErrorHandler함수를 작성하고 각 catch 블록에서 호출하십시오.

나에게 묻는다면 두 번째 예제 ( ifis키워드 포함)는 프로젝트의 유지 보수 단계에서 읽기가 쉽지 않으며 오류가 발생하기 쉽습니다.

유지 보수 단계는 프로그래밍에 익숙하지 않은 사용자를 위해 프로젝트 전체 수명의 98.7 % 이상을 차지할 것이며 유지 보수를 수행하는 불량 Schmuck은 거의 다른 사람이 될 것입니다. 그리고 그들이 당신의 이름을 저주하는 일에 그들의 시간의 50 %를 소비 할 가능성이 매우 높습니다.

그리고 물론의 FxCop 당신의 당신이 할 필요가 있으므로 껍질 정확하게 실행중인 프로그램을 할 압축 한 코드에 속성을 추가 한 경우 99.9 %에 완전히이라고 문제를 무시의 FxCop 말씀 만이 신고가 정확합니다. 그리고, 죄송합니다, 착각 할 수도 있지만, 그 “무시”속성이 실제로 앱에 컴파일되지 않습니까?

전체 if테스트를 한 줄에 배치하면 더 읽기 쉬워 집니까? 나는 그렇게 생각하지 않습니다. 한 번에 한 줄에 더 많은 코드를 넣으면 “더 빠르게”실행될 것이라고 한 번도 다른 프로그래머가 격렬하게 주장했음을 의미합니다. 그러나 물론 그는 굶주린 견과였다. 통역사 또는 컴파일러가 어떻게 긴 줄을 별개의 한 줄에 한 줄로 나누는 방법을 그에게 설명하려고 노력했지만 본질적으로 결과가 동일한 경우 컴파일러를 능숙하게 만들지 않고 코드를 읽을 수있게 만들었습니다. 그러나 나는 산만하다.

한 달에서 두 달 사이에 예외 유형을 세 개 더 추가 할 때 읽기가 훨씬 어렵 습니까? (답 : 읽기 가 훨씬 어렵습니다).

실제로 중요한 점 중 하나는 우리가 매일보고있는 텍스트 소스 코드를 형식화하는 대부분의 포인트가 코드 실행시 실제로 발생하는 일을 다른 사람에게 실제로 분명하게 만드는 것입니다. 컴파일러는 소스 코드를 완전히 다른 것으로 변환하고 코드 서식 스타일에 대해서는 신경 쓰지 않기 때문입니다. 따라서 올인원 라인도 완전히 짜증납니다.

그냥 …

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


답변

다른 사람들이 지적했듯이 ifcatch 블록 내에 명령문을 사용하여 진행 상황을 결정할 수 있습니다. 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)
{
    
}

이것은 블록 if내에서 명령문 을 사용하는 것과 다릅니다. catch예외 필터 사용 하면 스택이 풀리지 않습니다 .

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

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

설치 패키지 Microsoft.Net.Compilers

작성 당시에는 C # 6에 대한 지원이 포함됩니다.

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


답변

불행히도 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();
}


답변

완전성을 위해 .NET 4.0 이후 코드는 다음과 같이 다시 작성할 수 있습니다.

Guid.TryParse(queryString["web"], out WebId);

TryParse 는 예외가 발생하지 않으며 형식이 잘못된 경우 WebId를로 설정하여 false를 반환합니다 Guid.Empty.


C # 7 부터는 별도의 행에 변수를 도입하지 않아도됩니다.

Guid.TryParse(queryString["web"], out Guid webId);

.NET Framework에서는 아직 버전 4.6에서는 사용할 수없는 반환 튜플을 구문 분석하는 메서드를 만들 수도 있습니다.

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

그리고 이것을 다음과 같이 사용하십시오 :

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

이 쓸모없는 답변에 대한 다음 쓸모없는 업데이트는 C # 12에서 매개 변수의 해체가 구현 될 때 발생합니다.


답변

예외 필터는 이제 C # 6+에서 사용할 수 있습니다. 넌 할 수있어

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

C # 7.0 이상에서는이를 패턴 일치와 결합 할 수도 있습니다

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}


답변

응용 프로그램을 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의 유일한 새로운 기능으로 이전 버전에서는 에뮬레이션 할 수 없습니다. 우선, 다시 던지기는 캐치를 건너 뛰는 것보다 더 많은 오버 헤드를 의미합니다. 둘째, 의미 상 동등하지 않습니다. 새로운 기능은 코드를 디버깅 할 때 스택을 그대로 유지합니다. 이 기능이 없으면 크래시 덤프는 덜 유용하거나 쓸모가 없습니다.

CodePlex에 대한 논의를 참조하십시오 . 그리고 차이점을 보여주는 예 입니다.