28

AggregateException をスローしてキャッチしようとしています。私は C# で例外をあまり使用しませんでしたが、私が見つけた動作は少し驚くべきものです。

私のコードは次のとおりです。

var numbers = Enumerable.Range(0, 20);

try
{
    var parallelResult = numbers.AsParallel()
        .Where(i => IsEven(i));
    parallelResult.ForAll(e => Console.WriteLine(e));

}
catch (AggregateException e)
{
    Console.WriteLine("There was {0} exceptions", e.InnerExceptions.Count());
}

関数 IsEven を呼び出しています

private static bool IsEven(int i)
{
    if (i % 10 == 0)
        throw new AggregateException("i");
    return i % 2 == 0;
}

それは AggregateException をスローします。

私は、コードが 0,20 の範囲内のすべての偶数と「1 つの例外がありました」を 2 回書き込むことを期待します。

私が得たのは、いくつかの数字が出力され(ForAllのランダムな原因です)、例外がスローされましたが、キャッチされず、プログラムが停止しました。

何か不足していますか?

4

2 に答える 2

8

私は他の人に同意します。これは .Net のバグであり、報告する必要があります。

原因はQueryEnd()内部クラスのメソッドにありQueryTaskGroupStateます。その逆コンパイルされた (そしてわかりやすくするために少し変更された) コードは次のようになります。

try
{
  this.m_rootTask.Wait();
}
catch (AggregateException ex)
{
  AggregateException aggregateException = ex.Flatten();
  bool cacellation = true;
  for (int i = 0; i < aggregateException.InnerExceptions.Count; ++i)
  {
    var canceledException =
        aggregateException.InnerExceptions[i] as OperationCanceledException;
    if (IsCancellation(canceledException))
    {
      cacellation = false;
      break;
    }
  }
  if (!cacellation)
    throw aggregateException;
}
finally
{
  this.m_rootTask.Dispose();
}
if (!this.m_cancellationState.MergedCancellationToken.IsCancellationRequested)
  return;
if (!this.m_cancellationState.TopLevelDisposedFlag.Value)
  CancellationState.ThrowWithStandardMessageIfCanceled(
    this.m_cancellationState.ExternalCancellationToken);
if (!userInitiatedDispose)
  throw new ObjectDisposedException(
    "enumerator", "The query enumerator has been disposed.");

基本的に、これが行うことは次のとおりです。

  • AggregateExceptionキャンセル以外の例外が含まれている場合は、フラット化されたものを再スローします
  • キャンセルが要求された場合、新しいキャンセル例外をスローします (または、スローせずに戻ります。その部分はよくわかりませんが、ここでは関係ないと思います)
  • ObjectDisposedExceptionそうでなければ、何らかの理由でスローします( is であると仮定するuserInitiatedDisposefalse、それはです)

したがって、AggregateException内部例外なしで をスローすると、空exのを含む になります。を呼び出すと、これは単なる空のになります。つまり、キャンセル以外の例外が含まれていないため、コードの最初の部分はこれをキャンセルと見なし、スローしません。AggregateExceptionAggregateExcaptionFlatten()AggreateException

しかし、コードの 2 番目の部分は、これが取り消しではないことを認識するため、完全に偽の例外をスローします。

于 2013-10-15T23:03:41.810 に答える