한 번에 여러 예외를 포착하시겠습니까?
-
02-07-2019 - |
문제
그냥 잡는 것은 비추천 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
그에 따라 실행됩니다) :
또한, 구체적으로 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 () 블록에서 여러 예외 유형을 잡을 수 없다는 점에서 중복 코드를 쓰지 않으려면 어떻게합니까?
낙진 값이 구성하기에 저렴한 특정 샘플을 감안할 때 다음 단계를 따르고 싶습니다.
- 웨이브를 낙하 값으로 초기화합니다.
- 임시 변수로 새로운 안내서를 구성하십시오.
- 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");
}
}