while 루프를 사용하지 않고 가장 안쪽의 예외를 찾으십니까?


82

C #에서 예외가 발생하면 내부 예외가있을 수 있습니다. 내가 원하는 것은 가장 안쪽의 예외, 즉 내부 예외가없는 리프 예외를 얻는 것입니다. while 루프에서이 작업을 수행 할 수 있습니다.

while (e.InnerException != null)
{
    e = e.InnerException;
}

그러나 나는 이것을 대신 사용할 수있는 한 줄짜리가 있는지 궁금했습니다.


4
내 클래스 중 하나에서 예외를 던지고 있지만 모든 예외를 삼키고 자체를 던지는 라이브러리에서 클래스를 사용하고 있습니다. 문제는 라이브러리 예외가 매우 일반적이며 문제를 처리하는 방법을 알기 위해 어떤 특정 예외를 던 졌는지 알아야합니다. 설상가상으로 라이브러리는 임의의 깊이로 서로 중첩 된 자체 예외를 여러 번 던집니다. 예를 들어 LibraryException -> LibraryException -> LibraryException -> MyException. 내 예외는 항상 체인의 마지막이며 자체 내부 예외가 없습니다.
— Daniel T.

오, 나는 당신이 왜 가장 안쪽으로 내려갈 수 있는지 알지만 (그리고 특정 유형에서 파생되는 가장 안쪽과 같은 변형과 같은) 이미 가지고있는 문제에 대해 이해하지 못합니다.
— Jon Hanna

오, 무슨 말인지 알겠습니다. 나는 그것을 쓰는 다른 방법이 있는지 궁금했습니다. 나는 LINQ루프를 작성해야 할 때 이상하게 보일 정도로 거의 모든 것을 사용 하는 데 너무 익숙해졌습니다 . 저는 주로 데이터 액세스로 작업하므로 ICollections거의 모든 작업을 수행합니다.
— Daniel T.

@Daniel, LINQ코드가 여전히 본질적으로 반복되어야한다는 사실을 마스킹 하지 않습니까? .NET에 진정한 세트 기반 명령이 있습니까?
— Brad

6
가장 정답은 (현재) Exception.GetBaseException ()이라는 단 2 표로 바닥 근처에 숨어 있습니다. 이것은 기본적으로 영원히 프레임 워크에있었습니다. 온 전성 검사를 위해 batCattle에게 감사드립니다.
— Jay

답변:


138

짧막 한 농담 :)

while (e.InnerException != null) e = e.InnerException;

분명히 더 간단하게 만들 수는 없습니다.

Glenn McElhoe 의이 답변 에서 말했듯이 이것이 유일한 신뢰할 수있는 방법입니다.


7
GetBaseException () 메소드는 루프없이이 작업을 수행합니다. msdn.microsoft.com/en-us/library/…
— codingoutloud

8
이것은 작동하지만 Exception.GetBaseException()나를 위해 그렇지 않았습니다.
— Tarik 2013 년

121

나는 Exception.GetBaseException()이러한 솔루션과 동일한 일을 한다고 생각 합니다.

주의 사항 : 다양한 의견을 통해 우리는 항상 문자 그대로 같은 일을 . 어떤 경우에는 재귀 / 반복 솔루션이 더 입니다. 그것은이다 일반적으로 , 기본을 무시 예외 특정 유형의 덕분에 실망 일관성이 가장 안쪽의 예외입니다. 그러나 특정 유형의 예외를 포착하고 (AggregateException과 같은) 이상한 것이 아닌지 합리적으로 확인하면 합법적 인 가장 안쪽 / 가장 빠른 예외가 발생할 것으로 예상합니다.


3
+1 복잡한 솔루션을 피하기 위해 BCL을 아는 것만 큼 좋은 것은 없습니다. 분명히 이것이 가장 정답입니다.
— Jay

4
어떤 이유로 GetBaseException()첫 번째 루트 예외를 반환하지 않았습니다.
— Tarik

4
Glenn McElhoe는 실제로 GetBaseException ()이 MSDN에서 우리가 일반적으로 예상 할 수있는 일을 항상 수행하는 것은 아니라는 점을 지적했습니다 ( "하나 이상의 후속 예외의 근본 원인 인 예외"). AggregateException에서는 동일한 유형의 가장 낮은 예외로 제한되며 다른 유형도있을 수 있습니다.
— Josh Sutterfield 2014 년

1
내 문제에 대해 작동하지 않습니다. 나는 이것이 제공하는 것보다 몇 가지 더 낮은 수준이 있습니다.
— Giovanni B

2
GetBaseException()가장 중요한 예외는 AggregateException이기 때문에 나를 위해 작동하지 않았습니다.
— Chris Ballance 15.10.07

21

InnerExceptions를 통해 반복하는 것이 유일한 신뢰할 수있는 방법입니다.

발견 된 예외가 AggregateException이면 GetBaseException()가장 안쪽에있는 AggregateException 만 반환합니다.

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx


1
잘 잡았습니다. 감사합니다-위의 답변이 일부 사람들에게 효과가 없었던 부분을 설명합니다. 이것은 "하나 이상의 후속 예외의 근본 원인"에 대한 MSDN의 일반적인 설명이 파생 클래스가이를 재정의 할 수 있으므로 항상 가정하는 것이 아님을 분명히합니다. 실제 규칙은 예외 체인의 모든 예외가 GetBaseException ()에서 "동의"하고 동일한 객체를 반환한다는 것입니다. 이는 AggregateException의 경우처럼 보입니다.
— Josh Sutterfield 2014 년

12

내부 예외가 얼마나 깊이 중첩되어 있는지 모르는 경우 루프 또는 재귀를 피할 방법이 없습니다.

물론이를 추상화하는 확장 메서드를 정의 할 수 있습니다.

public static class ExceptionExtensions
{
    public static Exception GetInnermostException(this Exception e)
    {
        if (e == null)
        {
            throw new ArgumentNullException("e");
        }

        while (e.InnerException != null)
        {
            e = e.InnerException;
        }

        return e;
    }
}


2

때로는 내부 예외가 많을 수 있습니다 (많은 버블 예외). 어떤 경우에 다음을 수행 할 수 있습니다.

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}

1

한 줄은 아니지만 닫습니다.

        Func<Exception, Exception> last = null;
        last = e => e.InnerException == null ? e : last(e.InnerException);

1

사실 아주 간단합니다. Exception.GetBaseException()

Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try

솔루션에 감사드립니다! 소수의 사람들이 VB에서 생각 : D
— 페데리코 Navarrete은

1
그리고 그 이유가 있습니다! : P
— Yoda

1

반복해야하고 반복해야하므로 루프를 별도의 기능으로 이동하는 것이 더 깔끔합니다.

이를 처리하기 위해 확장 방법을 만들었습니다. 지정된 유형의 모든 내부 예외 목록을 반환하여 Exception.InnerException 및 AggregateException.InnerExceptions를 추적합니다.

내 특정 문제에서 내부 예외를 추적하는 것은 평소보다 더 복잡했습니다. 왜냐하면 리플렉션을 통해 호출되는 클래스의 생성자에 의해 예외가 발생했기 때문입니다. 우리가 잡은 예외에는 TargetInvocationException 유형의 InnerException이 있었고 실제로 살펴 봐야 할 예외는 트리 깊숙이 묻혀있었습니다.

public static class ExceptionExtensions
{
    public static IEnumerable<T> innerExceptions<T>(this Exception ex)
        where T : Exception
    {
        var rVal = new List<T>();

        Action<Exception> lambda = null;
        lambda = (x) =>
        {
            var xt = x as T;
            if (xt != null)
                rVal.Add(xt);

            if (x.InnerException != null)
                lambda(x.InnerException);

            var ax = x as AggregateException;
            if (ax != null)
            {
                foreach (var aix in ax.InnerExceptions)
                    lambda(aix);
            }
        };

        lambda(ex);

        return rVal;
    }
}

사용법은 매우 간단합니다. 예를 들어, 우리가

catch (Exception ex)
{
    var myExes = ex.innerExceptions<MyException>();
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
    {
        // ...
    }
}

무슨 일이야 Exception.GetBaseException()?
— Kiquenet

1

재귀를 사용하여 어딘가의 유틸리티 클래스에 메서드를 만들 수 있습니다.

public Exception GetFirstException(Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

사용하다:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = GetFirstException(ex);
}

제안 된 확장 방법 (좋은 아이디어 @dtb)

public static Exception GetFirstException(this Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

사용하다:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = ex.GetFirstException();
}

유용성 public static Exception GetOriginalException(this Exception ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); }
— Kiquenet 2015

적용되지 AggregateException.InnerExceptions않습니까?
— Kiquenet

0

또 다른 방법은 GetBaseException()두 번 호출하는 것입니다 .

Exception innermostException = e.GetBaseException().GetBaseException();

이면 AggregateException첫 번째 호출이 가장 안쪽이 아닌 곳으로 AggregateException이동하고 두 번째 호출이 해당 예외의 가장 안쪽 예외로 이동하기 때문에 작동합니다. 첫 번째 예외가 아닌 경우 AggregateException두 번째 호출은 동일한 예외를 반환합니다.


0

나는 이것을 만났고 예외 "스택"의 모든 예외 메시지를 나열 할 수 있기를 원했습니다. 그래서 저는 이것을 생각해 냈습니다.

public static string GetExceptionMessages(Exception ex)
{
    if (ex.InnerException is null)
        return ex.Message;
    else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.