문제

그냥 잡는 것은 비추천 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 ...
)

원래의:

나는 여기 파티에 조금 늦었다는 것을 알고 있지만 성스러운 연기 ...

체이스로 바로 자르면 이런 종류의 이런 종류의 복제는 이전 답변을 복제하지만, 여러 예외 유형에 대한 일반적인 작업을 수행하고 한 가지 방법의 범위 내에서 모든 것을 깔끔하게 유지하려면 Lambda를 사용하지 않는 이유는 무엇입니까? /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 기능 및 마찬가지로 각 캐치 블록에서 호출하십시오.

나에게 물어 보면 두 번째 예제 ( if 그리고 is 키워드)는 프로젝트의 유지 보수 단계에서 읽기 쉬운 덜 읽기 쉬우 며 동시에 오류가 발생하기 쉽습니다.

비교적 새로운 프로그래밍에 익숙하지 않은 사람의 경우 유지 보수 단계는 프로젝트의 전반적인 수명의 98.7% 이상을 구성 할 것이며, 유지 보수를하는 가난한 슈 머크는 거의 확실히 당신 이외의 다른 사람이 될 것입니다. 그리고 그들이 당신의 이름을 저주하는 직업에 시간의 50%를 보낼 가능성이 매우 높습니다.

물론 FXCOP는 당신에게 짖는 소리를냅니다. 또한 실행중인 프로그램과 정확하게 지퍼가있는 코드에 속성을 추가하고 FXCOP에 99.9%의 경우 플래그가 완전히 정확하다는 문제를 무시하도록 지시해야합니다. 그리고 죄송합니다. 실수를 할 수도 있지만 실제로 "무시"속성이 실제로 앱에 컴파일되지 않습니까?

전체를 넣을 것입니다 if 한 줄에서 테스트하면 더 읽기 쉬워 지나요? 나는 그렇게 생각하지 않습니다. 내 말은, 나는 또 다른 프로그래머가 한 번에 한 줄에 더 많은 코드를두면 "더 빨리 실행"할 것이라고 주장했다. 그러나 물론 그는 끔찍한 견과류였습니다. 통역사 나 컴파일러가 어떻게 긴 줄을 분리 된 1 번의 진문 당 명세서로 분리 할 것인지에 대해 그에게 설명하려고 노력하는 것 (똑바로 도전했다). 방금 컴파일러를 유출하려는 대신 코드를 읽을 수있게 만들었습니다. 그러나 나는 다투고있다.

얼마나 많이 더 적은 읽기 가능한 3 개 예외 유형을 추가하면 한 달 또는 두 번 더 추가 할 때 얻을 수 있습니까? (답 : a 많은 덜 읽기 쉬운).

실제로 주요 요점 중 하나는 우리가 매일보고있는 텍스트 소스 코드를 형식화하는 대부분의 요점은 코드가 실행될 때 실제로 일어나는 일을 다른 인간에게 실제로 명확하게 만드는 것입니다. 컴파일러가 소스 코드를 완전히 다른 것으로 바꾸고 코드 형식 스타일에 대해서는 신경 쓰지 않기 때문입니다. 그래서 일대일도 완전히 짜증납니다.

그냥 ...

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

다른 사람들이 지적했듯이 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)
{
    …
}

이것은 AN을 사용하는 것과 다릅니다 if 내에서의 진술 catch 예외 필터를 사용하여 블록 ~하지 않을 것이다 스택 풀기.

다운로드 할 수 있습니다 Visual Studio 2015 이것을 확인하려면.

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

설치 -Package Microsoft.net.compilers

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

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

불행히도 C#은 아닙니다. 예외 필터가 필요하기 때문에 C#은 MSIL의 기능을 노출시키지 않습니다. vb.net 은이 기능이 있습니다

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

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

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

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

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

tryparse 형식이 잘못되면 예외를 던지지 않고 거짓을 반환합니다. Guid.Empty.


부터 C# 7 별도의 라인에 변수를 도입하지 않을 수 있습니다.

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

또한 .NET 프레임 워크에서 사용할 수없는 반환 튜플을 구문 분석하는 방법을 만들 수도 있습니다. 버전 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으로 업그레이드할 수 있다면 행운입니다.새로운 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에서 이에 대한 토론.그리고 차이점을 보여주는 예.

이제 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 )
{
   //do something with members of ae, say ae.InnerExceptions
}

당신이 사용하고 싶지 않다면 if 내에서의 진술 catch 스코프, 안에 C# 6.0 당신이 사용할 수있는 Exception Filters 통사론 미리보기 버전에서 CLR에서 이미 지원되었지만 VB.NET/MSIL:

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

이 코드는 Exception a InvalidDataException 또는 ArgumentNullException.

실제로, 당신은 기본적으로 모든 조건을 그 안에 넣을 수 있습니다. when 절:

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

반대되는 경우에 주목하십시오 if 내부의 진술 catch의 범위, Exception Filters 던질 수 없습니다 Exceptions, 그리고 그들이 할 때, 또는 상태가 그렇지 않은 경우 true, 다음 catch 대신 조건이 평가됩니다.

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

출력 : 일반 캐치.

하나 이상이있을 때 true Exception Filter - 첫 번째는 받아 들여질 것입니다.

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

출력 : 캐치.

그리고 당신이 볼 수 있듯이 MSIL 코드는 변환되지 않습니다 if 진술, 그러나 Filters, 그리고 Exceptions 표시된 지역 내에서 던질 수 없습니다 Filter 1 그리고 Filter 2 그러나 필터는 Exception 대신 실패 할 것입니다. endfilter 명령은 필터의 성공/실패를 결정합니다 (Catch 1 xor Catch 2 그에 따라 실행됩니다) :

Exception Filters MSIL

또한, 구체적으로 Guid has the Guid.TryParse 방법.

CodeAnalysis/를 제외하고 허용 된 답변은 허용되는 것 같습니다.fxcop 일반적인 예외 유형을 포착하고 있다는 사실에 대해 불평 할 것입니다.

또한 "IS"연산자가 성능을 약간 저하시킬 수있는 것 같습니다.

CA1800 : 불필요하게 캐스트하지 마십시오 "대신 '연산자의 결과를 테스트하는 것을 고려하는 것"이라고 말하지만, 그렇게하면 각 예외를 개별적으로 포착하는 것보다 더 많은 코드를 작성할 것입니다.

어쨌든, 여기에 내가 할 일이 있습니다.

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

C# 6에서 권장되는 접근법은 예외 필터를 사용하는 것입니다. 예는 다음과 같습니다.

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }

C# 7 Michael Stum의 답변 스위치 문의 가독성을 유지하면서 개선 될 수 있습니다.

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

이것은 Matt의 답변의 변형입니다 (조금 더 깨끗하다고 ​​생각합니다)... 다음 방법을 사용하십시오.

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
}

조셉 다이 글의 대답 좋은 솔루션이지만, 다음 구조가 약간 더 깔끔하고 오류가 적다는 것을 알았습니다.

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

    // Handle exception
}

표현을 반전시키는 데 몇 가지 장점이 있습니다.

  • 반환 문은 필요하지 않습니다
  • 코드는 중첩되지 않았습니다
  • 요셉의 해결책에서 표현에서 분리되어 있다는 '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
}

@Micheal

코드의 약간 수정 된 버전 :

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

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

어때

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

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

링크의 내용은 귀하의 질문에 직접 답변되지 않지만 다음과 같은 모양으로 확장하는 것은 사소한 일입니다.

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

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

(기본적으로 또 다른 빈 공급을 제공합니다 Catch 자체를 반환하는 과부하)

이것에 대한 더 큰 질문은입니다 . 나는 비용이 여기서 이익을 능가한다고 생각하지 않습니다 :)

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

업데이트 2015-12-15 : 참조 https://stackoverflow.com/a/22864936/1718702 C#6의 경우. 그것은 더 깨끗하고 이제 언어의 표준입니다.

원하는 사람들을 위해 준비되어 있습니다 더 우아한 솔루션 한 번 잡아서 필터를 예외로하려면 아래에 표시된대로 확장 방법을 사용합니다.

나는 이미 내 도서관 에서이 확장을 원래 다른 목적으로 작성했지만 완벽하게 작동했습니다. type 예외를 확인합니다. 게다가, IMHO, 그것은 많은 것보다 깨끗해 보입니다. || 진술. 또한 허용 된 답변과 달리 명시 적 예외 처리를 선호합니다. ex is ... 데러 된 클래스가 부모 유형에 할당 될 수 있기 때문에 바람직하지 않은 행동이있었습니다).

용법

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

isanyof.cs Extension (종속성에 대한 전체 오류 처리 예 참조)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

전체 오류 처리 예제 (새 콘솔 앱에 대한 복사판)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

두 개의 샘플 수녀 단위 테스트

일치하는 행동 Exception 유형은 정확합니다 (예 : 어린이는 부모 유형과 일치하지 않습니다).

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

이 답변이 표면에 닿은 것처럼 느꼈기 때문에 조금 더 깊이 파고 들으려고했습니다.

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

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

우리가 원하는 이유는 예외 핸들러가 나중에 프로세스에서 필요한 것들을 잡기를 원하지 않기 때문입니다. 물론, 우리는 예외를 포착하고 '만약'해야 할 일을 확인할 수 있지만 솔직히 말하면 실제로는 원하지 않습니다. (FXCOP, 디버거 문제, 못생긴)

그렇다면 왜이 코드를 컴파일하지 않을 것입니다. 그리고 어떻게 우리는 그것을 어떻게 해킹 할 수 있습니까?

코드를 보면 실제로하고 싶은 것은 전화를 전달하는 것입니다. 그러나 MS Partition II에 따르면 IL 예외 핸들러 블록은 이와 같이 작동하지 않습니다.이 경우 '예외'객체가 다른 유형을 가질 수 있음을 의미하기 때문에 의미가 있습니다.

또는 코드로 작성하려면 컴파일러에게 이와 같은 일을하도록 요청합니다 (잘 맞지 않지만 가장 가까운 가능한 것입니다).

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

이것이 컴파일하지 않는 이유는 매우 명백합니다. '$ Exception'객체가 어떤 유형과 값을 가질 수 있습니까 (여기에 'e'에 저장된)? 컴파일러가이를 처리하기를 원하는 방식은 두 가지 예외의 공통 기본 유형은 '예외'라는 점에 유의하는 것입니다. 변수는 두 가지 예외를 모두 포함하는 데 사용한 다음 잡힌 두 가지 예외 만 처리합니다. 이것이 IL에서 구현되는 방식은 '필터'로 vb.net에서 사용할 수 있습니다.

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

    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 :-)");
}

그것은 단지 '재단'을 남깁니다. 이것이 작동하려면 '캐치'블록 내부에서 핸들링을 수행 할 수 있어야합니다.이 작업을 수행하는 유일한 방법은 '예외'개체를 사용하는 것입니다.

이 시점에서 오버로드 해상도를 사용하여 다양한 유형의 예외를 처리하거나 예외를 처리하는 별도의 기능을 추가 할 수 있습니다. 둘 다 단점이 있습니다. 시작하려면 다음과 같은 도우미 기능으로 수행하는 방법이 있습니다.

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.");

다른 솔루션은 예외 객체를 잡고 그에 따라 처리하는 것입니다. 위의 맥락을 바탕으로 이것에 대한 가장 문자적인 번역은 다음과 같습니다.

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와 디버거의 예외를 끊을 것입니다.

이것은 모든 C# 개발자가 결국 직면하는 고전적인 문제입니다.

질문을 두 가지 질문으로 나누겠습니다. 첫번째,

한 번에 여러 예외를 포착 할 수 있습니까?

요컨대, 아니요.

다음 질문으로 이어집니다.

동일한 catch () 블록에서 여러 예외 유형을 잡을 수 없다는 점에서 중복 코드를 쓰지 않으려면 어떻게합니까?

낙진 값이 구성하기에 저렴한 특정 샘플을 감안할 때 다음 단계를 따르고 싶습니다.

  1. 웨이브를 낙하 값으로 초기화합니다.
  2. 임시 변수로 새로운 안내서를 구성하십시오.
  3. WebID를 완전히 구성된 임시 변수로 설정하십시오. 이것을 시도 {} 블록의 최종 문으로 만드십시오.

따라서 코드는 다음과 같습니다.

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

예외가 발생하면 Webid는 절반으로 구성된 값으로 설정되지 않으며 안내를 유지합니다.

낙하 값을 구성하는 것이 비싸고 값을 재설정하는 것이 훨씬 저렴하다면 재설정 코드를 자체 기능으로 옮길 것입니다.

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}

그래서 당신은 모든 예외 스위치 내에서 많은 코드를 반복하고 있습니까? 방법을 추출하는 것처럼 들린다.

따라서 코드는 다음과 같습니다.

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에서 당신은 또한 예외 필터 다른 사람들이 이미 언급했듯이. 따라서 위의 코드를 다음과 같이 수정할 수 있습니다.

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

이 긴 스레드에 대한 짧은 대답을 추가하고 싶었습니다. 언급되지 않은 것은 Catch 문의 우선 순서입니다.보다 구체적으로 당신은 잡으려고하는 각 유형의 예외의 범위를 알고 있어야합니다.

예를 들어 "Catch-all"예외를 사용하는 경우 예외 그것은 다른 모든 캐치 문을 선포하고 분명히 컴파일러 오류가 발생하지만 순서를 뒤집으면 캐치 문장을 체인 할 수 있습니다. 예외 하단에 입력하면 시도에서 더 높은 곳을 충족시키지 못하는 예외를 캡처합니다. 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 문서를 검토하는 사람들을 강력히 추천합니다.

예외 계층

어획 조항 내부에 있지 않은 코드의 다른 부분에서와 같이 공통 코드를 메소드에 넣는 것과 같이 코드를 간단하게 유지하려고 노력할 수 있습니까?

예 :

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

// ...

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

내가 어떻게 할 것인지, 단순한 것이 아름답습니다 무늬

내가 할 수있는 한 가지 방법을 찾았지만 이것은 재료처럼 보입니다. 매일 WTF:

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

여기서 언급 할 가치가 있습니다. 여러 조합 (예외 오류 및 예외)에 응답 할 수 있습니다.

텍스트 상자, TextBlock 또는 Checkbox와 같은 컨텐츠를 사용하여 Datagrid에서 제어 객체를 캐스팅하려고 할 때 유스 케이스 시나리오를 만났습니다. 이 경우 반환 된 예외는 동일했지만 메시지는 다양했습니다.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

가장 짧은 대답을 제안하고 싶습니다 (하나 더 기능적 스타일):

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

이를 위해서는 System.action과 유사한 여러 "Catch"메소드 오버로드를 작성해야합니다.

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

그리고 당신이 원하는만큼. 그러나 한 번 수행해야하며 모든 프로젝트에서 사용할 수 있습니다 (또는 Nuget 패키지를 만든 경우 사용할 수 있습니다).

그리고 Catchmany 구현 :

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

추신 : 코드 단순성에 대해 null 검사를하지 않았으므로 매개 변수 유효성 검사를 추가하십시오.

PS2 캐치에서 값을 반환하려면 동일한 캐치 방법을 수행해야하지만 매개 변수의 작업 대신 반환 및 기능을 사용해야합니다.

시도해 전화하고 두 번 잡으십시오.

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

그것은 단순히 간단합니다 !!

C# 6.0에서 예외 필터는 예외 처리를위한 개선입니다.

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");
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top