首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >捕获AggregateException

捕获AggregateException
EN

Stack Overflow用户
提问于 2013-10-09 16:05:29
回答 2查看 29.8K关注 0票数 28

我正在试着抛出并接住一个AggregateException。我在C#上没有太多使用异常,但我发现的行为有点令人惊讶。

我的代码是:

代码语言:javascript
复制
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

代码语言:javascript
复制
private static bool IsEven(int i)
{
    if (i % 10 == 0)
        throw new AggregateException("i");
    return i % 2 == 0;
}

这就抛出了AggregateException。

我预计代码会写出0,20范围内的每个偶数,并且“有1个异常”两次。

我得到的是打印的一些数字(它们是ForAll的随机原因),然后异常被抛出,但没有被捕获,程序停止。

我是不是遗漏了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-10-16 05:20:00

这实际上是一种有趣的。我认为问题在于你以一种意想不到的方式使用了AggregateException,这导致了PLINQ代码内部的错误。

AggregateException的全部意义在于将可能在并行进程中同时(或几乎同时)发生的多个异常组合在一起。所以AggregateException应该至少有一个内部异常。但是您抛出的是new AggregateException("i"),它没有内部异常。PLINQ代码试图检查InnerExceptions属性,遇到某种错误(可能是NullPointerException),然后似乎进入了某种循环。这可以说是PLINQ中的一个bug,因为您使用的是AggregateException的有效构造函数,即使它是一个不寻常的构造函数。

正如其他地方指出的,抛出ArgumentException在语义上更正确。但是您可以通过抛出一个构造正确的AggregateException来获得您正在寻找的行为,例如,通过将IsEven函数更改为如下所示:

代码语言:javascript
复制
private static bool IsEven(int i)
{
    if (i % 10 == 0){
        //This is still weird
        //You shouldn't do this. Just throw the ArgumentException.
        throw new AggregateException(new ArgumentException("I hate multiples of 10"));
    }
    return i % 2 == 0;
}

我认为这个故事的寓意是,除非你真正知道自己在做什么,否则不要抛出AggregateException,特别是当你已经在某种并行或Task-based操作中时。

票数 26
EN

Stack Overflow用户

发布于 2013-10-16 07:03:42

我同意其他人的观点:这是.Net中的一个错误,你应该使用report it

原因在于内部类QueryTaskGroupState中的QueryEnd()方法。它的反编译代码(为清晰起见稍作修改)如下所示:

代码语言:javascript
复制
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 (假设userInitiatedDisposefalse,它是false)

<代码>F215

因此,如果抛出一个没有内部异常的AggregateExceptionex将是一个包含空AggregateExcaptionAggregateException。调用Flatten()将把它变成一个空的AggreateException,这意味着它不包含任何非取消异常,所以代码的第一部分认为这是取消,不会抛出异常。

但是代码的第二部分意识到这不是取消,所以它抛出了一个完全虚假的异常。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19266338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档