web-dev-qa-db-ja.com

whileループを使用せずに最も内側の例外を見つけますか?

C#が例外をスローすると、内部例外が発生する可能性があります。私がやりたいのは、最も内側の例外、つまり、内側の例外を持たない葉の例外を取得することです。 whileループでこれを行うことができます。

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

しかし、代わりにこれを行うために使用できるワンライナーがあるかどうか疑問に思っていました。

75
Daniel T.

ワンライナー:)

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

明らかに、これ以上簡単にすることはできません。

Glenn McElhoeによる この回答 で述べたように、それが唯一の信頼できる方法です。

123
Draco Ater

Exception.GetBaseException() はこれらのソリューションと同じことをすると信じています。

警告:さまざまなコメントから、alwaysは文字通り同じことを行わず、場合によっては再帰的/反復的なソリューションが得られないことがわかりましたさらにあなた。これは、通常は最も内側の例外であり、デフォルトをオーバーライドする特定の種類の例外のおかげで、残念ながら一貫性がありません。ただし、特定の種類の例外をキャッチし、それらが(AggregateExceptionのような)奇妙なものではないことを合理的に確認する場合、正当な最も内側/最も早い例外が取得されると予想されます。

117

InnerExceptionsをループすることが唯一の信頼できる方法です。

キャッチされた例外がAggregateExceptionである場合、GetBaseException()は最も内側のAggregateExceptionのみを返します。

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

17
Glenn McElhoe

内部の例外がどの程度深くネストされているかわからない場合、ループや再帰を回避する方法はありません。

もちろん、これを抽象化する拡張メソッドを定義できます。

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;
    }
}
12
dtb

これが古い投稿であることは知っていますが、ExceptionクラスのメソッドであるGetBaseException()を誰も提案していないことに驚いています。

catch (Exception x)
{
    var baseException = x.GetBaseException();
}

これは、.NET 1.1以降で使用されています。ここのドキュメント: http://msdn.Microsoft.com/en-us/library/system.exception.getbaseexception(v = vs.71).aspx

10
Matt

多くの場合、内部の例外が多数発生する場合があります(多くのバブル例外)。その場合は、次のことを実行できます。

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

実際、非常に単純なので、Exception.GetBaseException()を使用できます

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

1行ではなく、近い:

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

再帰を使用して、ユーティリティクラスのどこかにメソッドを作成できます。

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();
}
1
Brad

ループする必要があり、ループする必要があるので、ループを別の関数に移動する方が簡単です。

これに対処する拡張メソッドを作成しました。指定したタイプのすべての内部例外のリストを返し、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")))
    {
        // ...
    }
}
1
Jeff Dege