C#が例外をスローすると、内部例外が発生する可能性があります。私がやりたいのは、最も内側の例外、つまり、内側の例外がないリーフ例外を取得することです。これはwhileループで実行できます。
while (e.InnerException != null)
{
e = e.InnerException;
}
しかし、代わりにこれを行うために使用できるワンライナーがあるかどうか疑問に思いました。
C#が例外をスローすると、内部例外が発生する可能性があります。私がやりたいのは、最も内側の例外、つまり、内側の例外がないリーフ例外を取得することです。これはwhileループで実行できます。
while (e.InnerException != null)
{
e = e.InnerException;
}
しかし、代わりにこれを行うために使用できるワンライナーがあるかどうか疑問に思いました。
LINQ
はほとんどすべてを行うために使用することに慣れているので、ループを書かなければならないときは奇妙に思えます。私は主にデータアクセスを使用しているのでICollections
、ほとんどすべての作業を行っています。
LINQ
コードが本質的にループしなければならないという事実を隠しているだけではありませんか?.NETに真のセットベースのコマンドはありますか?
回答:
Exception.GetBaseException()
が、私には機能しませんでした。
私はException.GetBaseException()
これらの解決策と同じことをすると信じています。
警告:我々はそれを考え出してきた様々なコメントはしない、常に文字通り同じことを行うと、いくつかのケースでは、再帰的/反復ソリューションは、さらにあなたを取得します。これは通常、最も内側の例外であり、デフォルトをオーバーライドする特定のタイプの例外のおかげで、残念ながら一貫性がありません。ただし、特定の種類の例外をキャッチし、それらが奇妙なものではないことを合理的に確認した場合(AggregateExceptionなど)、正当な最も内側/最も早い例外が発生すると予想されます。
GetBaseException()
最初のルート例外を返しませんでした。
GetBaseException()
一番上の例外はAggregateExceptionであるため、機能しませんでした。
InnerExceptionsをループすることが、信頼できる唯一の方法です。
キャッチされた例外がAggregateExceptionの場合、GetBaseException()
最も内側のAggregateExceptionのみを返します。
http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx
内部例外がネストされている深さがわからない場合は、ループや再帰を回避する方法はありません。
もちろん、これを抽象化する拡張メソッドを定義することもできます。
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;
}
}
これが古い投稿であることは知っていますが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
一行ではありませんが、近いです:
Func<Exception, Exception> last = null;
last = e => e.InnerException == null ? e : last(e.InnerException);
実際にはとてもシンプルなので、あなたは使うことができます Exception.GetBaseException()
Try
//Your code
Catch ex As Exception
MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try
ループする必要があり、ループする必要があるため、ループを別の関数に移動する方がクリーンです。
これに対処するための拡張メソッドを作成しました。指定されたタイプのすべての内部例外のリストを返し、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()
?
再帰を使用して、ユーティリティクラスのどこかにメソッドを作成できます。
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(); }
AggregateException.InnerExceptions
か?
それを行う別の方法は、GetBaseException()
2回呼び出すことです。
Exception innermostException = e.GetBaseException().GetBaseException();
これが機能するのは、のAggregateException
場合、最初の呼び出しで最も内側の非に到達し、AggregateException
次に2番目の呼び出しでその例外の最も内側の例外に到達するためです。最初の例外が、でない場合、AggregateException
2番目の呼び出しは同じ例外を返すだけです。
LibraryException -> LibraryException -> LibraryException -> MyException
ます。私の例外は常にチェーンの最後であり、独自の内部例外はありません。