한 번에 여러 예외를 탐지하시겠습니까?
단순히 잡는 것은 권장되지 않습니다.System.Exception
대신 "알려진" 예외만 포착해야 합니다.
다음과 같은 불필요한 반복 코드가 발생할 수 있습니다.
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
두 를 모두 잡아내고 두 가지 예외만 수 이 있을까요?WebId = Guid.Empty
한 번만 전화 한번만?
주어진 예는 단순하기 때문에 단순합니다. 하지만 객체를 여러 번 수정하고 조작 중 하나가 예상대로 실패하면 "재설정"하려는 코드를 상상해 보십시오.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 ...
)
원본:
파티에 좀 늦은 건 알지만, 세상에...
본론으로 들어가서, 이런 종류의 답변은 이전의 답변과 동일하지만, 여러 예외 유형에 대해 공통적인 작업을 수행하고 한 가지 방법의 범위 내에서 전체를 깔끔하게 유지하려면 람다/닫기/인라인 함수를 사용하여 다음과 같은 작업을 수행하는 것이 어떻습니까?제 말은, 여러분이 그 폐쇄를 모든 곳에서 활용할 수 있는 별도의 방법으로 만들고 싶어한다는 것을 깨닫게 될 가능성이 꽤 높다는 것입니다.하지만 그렇게 하면 코드의 나머지 부분을 구조적으로 변경하지 않고도 매우 쉽게 할 수 있습니다.그렇죠?
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
각 캐치 블록에서 호출합니다.
예를 (이는 만당신나와묻면두, 번예시함께다는째에이게약와▁the▁if▁((예,시with함▁the께면▁me▁example▁second,)if
그리고.is
키워드)를 사용하면 프로젝트의 유지 관리 단계에서 읽기 쉽고 오류가 발생하기 쉽습니다.
유지보수 단계는 프로그래밍을 비교적 처음 하는 사람이라면 누구나 프로젝트의 전체 수명의 98.7% 이상을 차지할 것이고, 유지보수를 하는 가난한 사람들은 거의 틀림없이 여러분이 아닌 다른 사람이 될 것입니다.그리고 그들은 그들의 시간의 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;
}
다른 사람들이 지적했듯이, 당신은 당신을 가질 수 있습니다.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)
{
…
}
은 이은사것다다니릅과는하를 .if
내의 .catch
예외 필터를 사용하면 스택이 풀리지 않습니다.
Visual Studio 2015를 다운로드하여 확인할 수 있습니다.
Visual Studio 2013을 계속 사용하려면 다음 너겟 패키지를 설치할 수 있습니다.
Microsoft를 설치 - 패키지합니다.넷 컴파일러
이 패키지를 참조하면 시스템에 설치된 버전이 아닌 패키지에 포함된 C# 및 Visual Basic 컴파일러의 특정 버전을 사용하여 프로젝트를 빌드할 수 있습니다.
안타깝게도 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();
}
이제 예외 필터를 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..?
}
완전성을 위해서, 그 이후로.NET 4.0 코드는 다음과 같이 다시 작성할 수 있습니다.
Guid.TryParse(queryString["web"], out WebId);
TryParse는 예외를 발생시키지 않으며 형식이 잘못되면 false를 반환합니다. WebId를 다음으로 설정합니다.Guid.Empty
.
C# 7에서는 변수를 별도의 줄에 추가하지 않아도 됩니다.
Guid.TryParse(queryString["web"], out Guid webId);
에서 사용할 수 없는 반환 튜플을 구문 분석하는 방법을 만들 수도 있습니다.버전 4.6 이후의 NET Framework:
(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# 9에 대한 업데이트
C# 9에서 향상된 새로운 패턴 매칭 기능을 사용하여 예외 필터의 표현식을 단축할 수 있습니다.여러 예외를 파악하는 것은 간단합니다.
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception e) when (e is FormatException or OverflowException)
{
WebId = Guid.Empty;
}
C# 7을 사용하면 스위치 문의 가독성을 유지하면서 Michael Stum의 답변을 개선할 수 있습니다.
catch (Exception ex)
{
switch (ex)
{
case FormatException _:
case OverflowException _:
WebId = Guid.Empty;
break;
default:
throw;
}
}
Orace 코멘트 덕분에 폐기 변수를 생략하여 C#8로 이를 단순화할 수 있습니다.
catch (Exception ex)
{
switch (ex)
{
case FormatException:
case OverflowException:
WebId = Guid.Empty;
break;
default:
throw;
}
}
C# 8을 스위치 식으로 사용:
catch (Exception ex)
{
WebId = ex switch
{
_ when ex is FormatException || ex is OverflowException => Guid.Empty,
_ => throw ex
};
}
네케미아 호프만이 지적했듯이.후자의 예는 스택 추적의 손실을 야기합니다.위르겐 스타인블록이 설명한 확장 방법을 사용하여 다음과 같이 스택 트레이스를 캡처하면 이를 방지할 수 있습니다.
catch (Exception ex)
{
WebId = ex switch
{
_ when ex is FormatException || ex is OverflowException => Guid.Empty,
_ => throw ex.Capture()
};
}
public static Exception Capture(this Exception ex)
{
ExceptionDispatchInfo.Capture(ex).Throw();
return ex;
}
C# 9의 패턴 매칭 향상으로 두 스타일을 단순화할 수 있습니다.
catch (Exception ex)
{
switch (ex)
{
case FormatException or OverflowException:
WebId = Guid.Empty;
break;
default:
throw;
}
}
catch (Exception ex)
{
WebId = ex switch
{
_ when ex is FormatException or OverflowException => Guid.Empty,
_ => throw ex.Capture()
};
}
당신이 를 사용하기를 원하지 않는다면if
내의 catch
미리보기 버전에서 CLR이 이미 지원하지만 에만 존재하는 구문을 사용할 수 있습니다.VB.NET
/MSIL
:
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
는 이코는수니다합신을음다드▁the▁will를 잡을 것입니다.Exception
그것이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
번째 입니다: " 번 째 허 용 니 다 됩 첫 이 목 항 니 다 됩 허 용▁the 첫
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
가 하만필던지가는터지▁the▁throwing▁filter▁the▁but는.Exception
이며, " " ", ", " " 먼저 도 실패합니다.endfilter
에 따라됩니다(「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 」Catch 1
XOR Catch 2
그에 따라 실행됨):
또한, 구으로적.Guid
방법이 있습니다.
catch (Exception ex) when (ex is FormatException or OverflowException)
{
WebId = Guid.Empty;
}
또는
catch (Exception ex)
{
if (ex is not FormatException and not OverflowException)
throw;
WebId = Guid.Empty;
}
Code Analysis/FxCop이 일반적인 예외 유형에 걸린다는 사실에 대해 불평할 것이라는 점을 제외하면 수락된 답변은 받아들일 수 있는 것으로 보입니다.
또한 "is" 연산자가 성능을 약간 저하시킬 수 있습니다.
CA1800: 캐스트가 불필요하게 "대신 'as' 연산자의 결과를 테스트하는 것을 고려해보라"고 말하지 마십시오. 하지만 그렇게 하면 각 예외를 개별적으로 포착하는 것보다 더 많은 코드를 작성하게 됩니다.
어쨌든, 제가 할 일은 다음과 같습니다.
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");
}
이것은 Matt의 대답을 변형한 것입니다. (이것이 좀 더 깨끗하다고 생각합니다.) ... 방법을 사용하십시오.
public void TryCatch(...)
{
try
{
// something
return;
}
catch (FormatException) {}
catch (OverflowException) {}
WebId = Guid.Empty;
}
사항은 는 " 른예가면코실행다니됩드가발다생하외다▁code니실▁any됩행▁the"입니다.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's Answer는 좋은 해결책이지만, 저는 다음 구조가 조금 더 깔끔하고 오류가 발생하기 쉽다는 것을 알게 되었습니다.
catch(Exception ex)
{
if (!(ex is SomeException || ex is OtherException)) throw;
// Handle exception
}
식을 뒤집으면 다음과 같은 몇 가지 이점이는 다음과 같습니다.
- 반품 전표가 필요하지 않습니다.
- 코드가 중첩되지 않았습니다.
- 조셉의 솔루션에 있는 '던지기' 또는 '되돌리기' 문구가 표현과 구분되어 있다는 것을 잊어버릴 위험이 없습니다.
한 줄로 압축할 수도 있습니다(별로 예쁘지는 않지만).
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
}
@미힐
코드의 약간 수정된 버전:
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
자체를 반환하는 과부하)
이것에 대한 더 큰 질문은 왜냐는 것입니다.나는 여기서 얻는 것보다 비용이 더 많다고 생각하지 않습니다 :)
2015-12-15 업데이트: C#6은 https://stackoverflow.com/a/22864936/1718702 을 참조하십시오.그것은 더 깨끗하고 이제 언어의 표준이 되었습니다.
한 번 감지하고 예외를 필터링할 수 있는 보다 우아한 솔루션을 원하는 사람들을 위해 아래와 같이 확장 방법을 사용합니다.
원래 다른 목적으로 작성된 이 확장자는 이미 라이브러리에 있었지만, 완벽하게 작동했습니다.type
예외를 확인합니다.게다가, 임호, 그것은 많은 사람들보다 깨끗해 보입니다.||
진술들.또한 수용된 답변과 달리 명시적인 예외 처리를 선호하기 때문에ex is ...
파생된 클래스가 상위 유형에 할당될 수 있으므로 바람직하지 않은 동작을 수행했습니다.
사용.
if (ex.GetType().IsAnyOf(
typeof(FormatException),
typeof(ArgumentException)))
{
// Handle
}
else
throw;
IsAnyOf.cs 확장(의존성에 대한 전체 오류 처리 예제 참조)
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));
}
}
}
두 개의 UNIT 단위 검정 예제
에 대한 일치 :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*/
}
}
}
}
한 가지 방법을 찾았지만, 이것은 데일리 WTF의 자료에 더 가깝습니다.
catch (Exception ex)
{
switch (ex.GetType().Name)
{
case "System.FormatException":
case "System.OverflowException":
WebId = Guid.Empty;
break;
default:
throw;
}
}
이 답들이 그저 표면에 닿은 것처럼 느껴졌기 때문에, 저는 조금 더 깊이 파고들려고 했습니다.
그래서 우리가 정말로 하고 싶은 것은 컴파일되지 않는 것입니다.
// 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에서 사용할 수 있는 '필터'입니다.그물.
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.");
그리고 다른 해결책은 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과 당신의 디버거의 잡히지 않은 예외들을 깨는 '예외' 캐치 핸들러에 코드를 넣을 수밖에 없습니다.
이것은 모든 C# 개발자가 결국 직면하는 고전적인 문제입니다.
질문을 두 가지 질문으로 나누겠습니다.첫번째,
한 번에 여러 예외를 파악할 수 있습니까?
간단히 말해서, 아닙니다.
다음 질문으로 이어집니다.
동일한 catch() 블록에서 여러 예외 유형을 탐지할 수 없는 경우 중복 코드를 작성하지 않으려면 어떻게 해야 합니까?
폴백 값이 저렴한 귀사의 구체적인 샘플을 고려할 때, 저는 다음 단계를 따르고 싶습니다.
- WebId를 폴백 값으로 초기화합니다.
- 임시 변수에 새 Guid를 구성합니다.
- WebId를 완전히 구성된 임시 변수로 설정합니다.try{} 블록의 마지막 문장으로 만듭니다.
코드는 다음과 같습니다.
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는 절반으로 설정되지 않으며 Guid로 유지됩니다.빈.
폴백 값을 구성하는 데 비용이 많이 들고 값을 재설정하는 것이 훨씬 저렴하다면 재설정 코드를 자체 기능으로 이동할 것입니다.
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" 예외를 Exception으로 사용하면 다른 모든 catch 문 앞에 표시되며 컴파일러 오류가 발생하지만 순서를 거꾸로 사용하면 catch-all Exception 유형을 맨 아래에 둘 수 있습니다.당신의 시도에서 더 높은 곳에 맞추지 못했습니다.캐치 블록:
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()
{
// ...
}
내가 어떻게 할 것인가, 단순하고 아름다운 패턴을 찾으려고 노력하는 것.
여기서 언급할 가치가 있습니다.여러 조합(예외 오류 및 예외.message)에 응답할 수 있습니다.
TextBox, TextBlock 또는 CheckBox와 같은 콘텐츠를 사용하여 데이터 그리드에서 제어 개체를 캐스트할 때 사용 사례 시나리오가 발생했습니다.이 경우 반환된 예외는 동일하지만 메시지는 다양합니다.
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과 유사한 "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));
}
원하는 만큼 얼마든지 가능합니다.그러나 한 번만 실행하면 모든 프로젝트에서 사용할 수 있습니다(또는 너트 패키지를 만든 경우에도 사용할 수 있습니다).
그리고 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;
}
}
p.s. 코드 단순성을 위해 null 검사를 하지 않았습니다. 매개 변수 유효성 검사를 추가하는 것을 고려해 보십시오.
p.s.2 캐치에서 값을 반환하려면 동일한 캐치 방법을 수행해야 하지만 매개 변수에 Action 대신 Func를 반환해야 합니다.
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception ex)
{
string ExpTyp = ex.GetType().Name;
if (ExpTyp == "FormatException")
{
WebId = Guid.Empty;
}
else if (ExpTyp == "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");
}
}
언급URL : https://stackoverflow.com/questions/136035/catch-multiple-exceptions-at-once
'programing' 카테고리의 다른 글
Azure 서비스 패브릭 대 Azure 컨테이너 서비스 (0) | 2023.05.09 |
---|---|
AADSTS70005: 애플리케이션에 대해 response_type 'id_token'이(가) 사용되도록 설정되지 않았습니다. (0) | 2023.05.09 |
Django Rest Framework 일련 번호의 집계(및 주석이 달린 기타) 필드 (0) | 2023.05.09 |
날짜 범위를 순환하려면 어떻게 해야 합니까? (0) | 2023.05.09 |
iOS 앱에서 대상의 버전/빌드 번호를 프로그래밍 방식으로 표시하는 방법은 무엇입니까? (0) | 2023.05.09 |