예외에 대한 추가 정보를 어떻게 제공해야합니까?


20

예외에 대한 추가 정보를 제공해야 할 때마다 실제로 어떤 방법이 올바른 방법 인지 궁금 합니다.


이 질문을 위해 예를 썼습니다. Abbreviation속성 을 업데이트하려는 클래스가 있다고 가정 해 봅시다 . SOLID 관점에서 보면 완벽하지는 않지만 일부 서비스를 통해 DI를 통해 작업자 메서드 를 전달하더라도 동일한 상황이 발생합니다. 예외가 발생하면 컨텍스트가 없습니다. 예제로 돌아 가기 ...

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Abbreviation { get; set; }
}

그런 다음 클래스의 인스턴스와 작업자 메서드가 호출되는 루프가 있습니다. 던질 수 있습니다 StringTooShortException.

var persons =
{
    new Person { Id = 1, Name = "Fo" },
    new Person { Id = 2, Name = "Barbaz" },
}

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // ?
        }
    }
    // throw AggregateException...
}

public IEnumerable<string> GenerateAbbreviation(string value)
{
    if (value.Length < 5)
    {
        throw new StringTooShortException(value);
    }

    // generate abbreviation
}

질문은 : Person또는 그 Id(또는 다른 것) 를 추가하는 방법은 무엇입니까?


다음 세 가지 기술을 알고 있습니다.


1- Data속성 사용

장점 :

  • 추가 정보를 쉽게 설정
  • 더 많은 예외를 만들 필요가 없습니다
  • 추가가 필요하지 않습니다 try/catch

단점 :

  • 에 쉽게 통합 될 수 없습니다 Message
  • 로거는이 필드를 무시하고 덤프하지 않습니다.
  • 키와 캐스팅이 필요하기 때문에 값이 object
  • 불변의

예:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            ex.Data["PersonId"] = person.Id;
            // collect ex
        }
    }
    // throw AggregateException...
}

2-사용자 정의 특성 사용

장점 :

  • Data속성 과 유사 하지만 강력한 유형
  • 에 쉽게 통합 Message

단점 :

  • 맞춤 예외가 필요합니다
  • 로거는 그들을 무시합니다
  • 불변의

예:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // not suitable for this exception because 
            // it doesn't have anything in common with the Person
        }
    }
    // throw AggregateException...
}

3-예외를 다른 예외로 랩

장점 :

  • Message 예측 가능한 방식으로 형식화 가능
  • 로거는 내부 예외를 덤프합니다
  • 불변의

단점 :

  • 추가가 필요합니다 try/catch
  • 중첩 증가
  • 집행의 깊이를 증가

예:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            try
            {
                person.Abbreviation = GenerateAbbreviation(person.Name);
            }
            catch(Exception ex)
            {
                throw new InvalidPersonDataException(person.Id, ex);
            }
        }
        catch(Exception ex)
        {
            // collect ex
        }
    }
    // throw AggregateException...
}

  • 다른 패턴이 있습니까?
  • 더 나은 패턴이 있습니까?
  • 일부 / 모두 모범 사례를 제안 할 수 있습니까?

C #의 예외에 익숙하지 않지만 일반적으로 예외가 발생할 때 Person 인스턴스가 여전히 유효 할 것으로 예상합니다. 당신은 그것을 시도 했습니까?
John Kouraklis

1
@ JohnKouraklis 이것은 질문에 관한 것이 아닙니다. ;-) 추가 정보로 의미하는 바를 보여주는 매우 간단한 예입니다. 여기에 mutliple 메소드가 예외를 던질 수 있고 컨텍스트 정보의 여러 레벨을 제공 해야하는 전체 프레임 워크를 게시하면 아무도 이것을 읽을 수 없으며 설명하기가 정말 어려웠습니다.
t3chb0t 2016

@JohnKouraklis 방금 데모 목적으로 만들었습니다.
t3chb0t 2016

@ t3chb0t 나는 당신이 여기에 당신의 자신의 질문에 대답했다고 생각합니다. 1, 2, 3을 답으로 옮기고 질문을 조정하여 내 의견에 따라 스타일을 선택하도록 요구하지 마십시오.
candied_orange

맞춤 예외에 어떤 문제가 있습니까? 올바르게 수행하면 도메인 언어의 일부이며 구현 세부 사항에서 추상화를 달성하는 데 도움이됩니다.
RubberDuck

답변:


6

Data FTW .

"대비":

  • "메시지에 쉽게 통합 될 수 없습니다"

->의 경우 귀하의 예외 유형은 우선 쉬운 충분해야한다 Message그래서 그 않는 법인 (法人)을 Data(가) 만약 내가에만이 고려 것이지만 .. Data메시지입니다 .

  • "로거는이 필드를 무시하고 덤프하지 않습니다"

일례로 인터넷 검색을 Nlog 수율 :

예외 레이아웃 렌더러

(...)

format- 출력 형식. 예외 특성 쉼표로 구분이어야 : Message, Type, ShortType, ToString, Method, StackTraceData. 이 매개 변수 값은 대소 문자를 구분하지 않습니다. 기본:message

따라서 쉽게 구성 할 수있는 것 같습니다.

  • 값이 객체이기 때문에 키와 캐스팅이 필요합니다

응? 거기에있는 객체를 덤프하고 사용 가능한 ToString()방법 이 있는지 확인하십시오 .

또한 키에 아무런 문제가 없습니다. 온화한 독창성을 사용하면 좋습니다.


면책 조항 : 이것은 질문에서 즉시 볼 수있는 것과 Data15 분 안에 봤습니다 . 나는 그것이 약간 도움이된다고 생각했기 때문에 대답으로 내놓았지만 결코 사용하지 않았 Data으므로 여기 질문자가 나보다 이것에 대해 더 많이 알고있을 것입니다.


유용한 예외와 이름 및 메시지에는 두 가지만 있다는 결론에 도달했습니다. 그 밖의 모든 것은 단순히 쓸데없는 소음이기 때문에 무시하기 쉬우므로 무시해야합니다.
t3chb0t

2

왜 예외를 던지나요? 그들이 잡히고 처리하도록.

catch 코드 는 예외 처리 방법어떻게 해결 합니까? Exception 객체에 정의한 속성 사용

Message 속성을 사용하여 예외를 식별하거나 잠재적 인 처리기가 의존해야하는 "정보"를 제공하지 마십시오. 단순히 너무 휘발성이고 신뢰할 수 없습니다.

전에는 "Data"속성을 사용한 적이 없지만 지나치게 일반적으로 들립니다.

생성하지 않는 한 많은 식별 각각의 예외의 클래스, 특정 예외적 인 경우를 어떻게 알 수 있습니까 당신은 예외 잡을 때 "데이터」를 나타냅니다 무엇을? ( "메시지"에 대한 이전 주석 참조).


1
나는 Data처리에 쓸모가 없지만 Message지옥의 서식 을 피하기 위해 로깅에 가치가 있다고 말합니다 .
Martin Ba

-1

나는 세 번째 예제를 좋아하지만, 대부분의 "con"을 제거하기 위해 코딩 할 수있는 또 다른 방법이 있습니다.

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    var exceptions = new List<InvalidPersonDataException>();

    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            exceptions.Add(new InvalidPersonDataException(person.Id, ex));
        }
    }

    if (exceptions.Any())
    {
        throw new AggregateException(exceptions);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.