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.