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


84

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

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

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


4
クラスの1つで例外をスローしていますが、私のクラスは、すべての例外を飲み込んで独自の例外をスローするライブラリによって使用されています。問題は、ライブラリの例外が非常に一般的であり、問​​題の処理方法を知るために、どの特定の例外をスローしたかを知る必要があることです。さらに悪いことに、ライブラリは、任意の深さまで、互いにネストされた独自の例外を複数回スローします。したがって、たとえば、をスローしLibraryException -> LibraryException -> LibraryException -> MyExceptionます。私の例外は常にチェーンの最後であり、独自の内部例外はありません。
ダニエルT.

ああ、私はあなたが自分でそうして(そして特定のタイプから派生した最も内側のような変種)、あなたが最も内側に降りるかもしれない理由を理解しますが、あなたがすでに持っているものの問題が何であるかわかりません。
ジョンハンナ

ああ、私はあなたが何を意味するのかわかります。別の書き方があるのか​​と思っていました。私LINQはほとんどすべてを行うために使用することに慣れているので、ループを書かなければならないときは奇妙に思えます。私は主にデータアクセスを使用しているのでICollections、ほとんどすべての作業を行っています。
ダニエルT.

@Daniel、LINQコードが本質的にループしなければならないという事実を隠しているだけではありませんか?.NETに真のセットベースのコマンドはありますか?
ブラッド

6
最も正しい答えは、(現在)わずか2票で最下部近くに潜んでいます-Exception.GetBaseException()。これは基本的に永遠にフレームワークにあります。健全性チェックをしてくれたbatCattleに感謝します。
ジェイ

回答:


140

一発ギャグ :)

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

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

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


7
GetBaseException()メソッドは、これをoaループで実行します。msdn.microsoft.com/en-us/library/...
codingoutloud

8
これは機能しますException.GetBaseException()が、私には機能しませんでした。
タリック2013年

122

私はException.GetBaseException()これらの解決策と同じことをすると信じています。

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


3
+1複雑なソリューションを回避するためにBCLを知ることに勝るものはありません。明らかに、これが最も正しい答えです。
ジェイ

4
何らかの理由で、GetBaseException()最初のルート例外を返しませんでした。
タリック2013年

4
Glenn McElhoeは、実際、GetBaseException()は、MSDNが一般的に期待できることを常に実行するとは限らないと指摘しました(「1つ以上の後続の例外の根本原因である例外」)。AggregateExceptionでは、同じタイプの最も低い例外に制限されており、おそらく他にもあります。
Josh Sutterfield 2014年

1
私の問題では機能しません。私はこれが提供しているものよりもいくつかレベルが下がっています。
ジョバンニB

2
GetBaseException()一番上の例外はAggregateExceptionであるため、機能しませんでした。
クリスバランス2015年

22

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

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

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


1
良いキャッチ。ありがとう-それはその答えが何人かの人々のために働いていなかった上記のコメントを説明します。これにより、MSDNの「1つ以上の後続の例外の根本原因」の一般的な説明が、派生クラスがオーバーライドする可能性があるため、必ずしも想定したものとは限らないことが明確になります。実際のルールは、一連の例外内のすべての例外が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;
    }
}

10

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

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

これは、.NET1.1以降に存在します。ここのドキュメント:http//msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v = vs.71).aspx


確かに、2012年12月5日に以前に示されました
armadillo.mx

2

多くの内部例外(多くのバブル例外)がある場合があります。その場合、あなたはしたいかもしれません:

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = 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
Federico Navarrete 2018年

1
そして、その理由があります!:P
ヨーダ

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 2018

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 2018

0

それを行う別の方法は、GetBaseException()2回呼び出すことです。

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

これが機能するのは、のAggregateException場合、最初の呼び出しで最も内側の非に到達し、AggregateException次に2番目の呼び出しでその例外の最も内側の例外に到達するためです。最初の例外が、でない場合、AggregateException2番目の呼び出しは同じ例外を返すだけです。


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.