【问题标题】:Throwing multiple exceptions in .Net/C#在 .Net/C# 中引发多个异常
【发布时间】:2010-09-21 15:56:24
【问题描述】:

在我处理的应用程序中,任何业务逻辑错误都会引发异常,调用代码会处理异常。这种模式在整个应用程序中都使用并且运行良好。

我有一种情况,我将尝试从业务层内部执行一些业务任务。对此的要求是一项任务的失败不应导致进程终止。其他任务应该仍然能够执行。换句话说,这不是原子操作。我遇到的问题是,在操作结束时,我希望通过抛出异常来通知调用代码确实发生了一个或多个异常。考虑以下伪代码 sn-p:

function DoTasks(MyTask[] taskList)
{
  foreach(MyTask task in taskList)
  {
    try
    {
       DoTask(task);
    }
    catch(Exception ex)
    {
        log.add(ex);
    }
  }

  //I want to throw something here if any exception occurred
}

我扔什么?我以前在职业生涯中遇到过这种模式。过去我保留了所有异常的列表,然后抛出一个包含所有捕获的异常的异常。这似乎不是最优雅的方法。尽可能多地保留每个异常的详细信息以呈现给调用代码,这一点很重要。

想法?


编辑:解决方案必须用 .Net 3.5 编写。我不能使用任何 beta 库,或者 Bradley Grainger(下文)提到的 .Net 4.0 中的 AggregateException 将是抛出集合异常的一个很好的解决方案。

【问题讨论】:

  • 只需重新抛出异常。在你记录它(并做任何其他事情)之后,重新抛出它,让它在堆栈中冒泡。
  • 哪个例外?我的场景预设了多个例外。
  • 我刚刚更新了我的答案,指出您可以在 .NET 3.5 上克隆 AggregateException,以便在发布时轻松升级到真正的 .NET 4.0 类型。您仍然需要编写代码(克隆类),但它是向前兼容的。
  • 布拉德利,我看到了。我想我会采取类似的方法。我正在编写一个名为 Collat​​edException 的类,因此它具有不同的名称,但我在 AggregateException 对其进行建模。 4.0出来的时候我可能会替换它。
  • @Jason Jackson:这听起来是个不错的选择。如果事实证明最终的 .NET 4.0 类不合适/不能使用,您的代码可以保持不变。否则,应该很容易重构以将您的代码与最新框架统一。

标签: c# .net exception


【解决方案1】:

这里没有超级优雅的解决方案,但有一些想法:

  • 将错误处理函数作为参数传递给 DoTasks,以便用户决定是否继续
  • 使用跟踪记录发生的错误
  • 在异常包的消息中连接来自其他异常的消息

【讨论】:

    【解决方案2】:

    您可能希望使用BackgroundWorker 为您执行此操作。它会在完成时自动捕获并呈现任何异常,然后您可以抛出或记录或执行任何操作。此外,您还可以享受多线程带来的好处。

    BackgroundWorker 是代理 asynchronous programming model. 的一个很好的包装器

    【讨论】:

    • 这个思路不错,但是聚合和重新抛出的问题还是存在的。此外,我的伪代码是对实际多线程场景的简化,其中可以从后台线程中检索多个异常。我只是不想将问题与该信息混淆。
    【解决方案3】:

    您可以创建一个自定义异常,该异常本身具有一组异常。然后,在您的 Catch 块中,将其添加到该集合中。在您的流程结束时,检查异常计数是否 > 0,然后抛出您的自定义异常。

    【讨论】:

    • 这就是问题本身给出的答案。
    【解决方案4】:

    我想到的两种方法是创建一个自定义异常并将异常添加到此类并将其抛出:

    public class TaskExceptionList : Exception
    {
        public List<Exception> TaskExceptions { get; set; }
        public TaskExceptionList()
        {
            TaskExceptions = new List<Exception>();
        }
    }
    
        public void DoTasks(MyTask[] taskList)
        {
            TaskExceptionList log = new TaskExceptionList();
            foreach (MyTask task in taskList)
            {
                try
                {
                    DoTask(task);
                }
                catch (Exception ex)
                {
                    log.TaskExceptions.Add(ex);
                }
            }
    
            if (log.TaskExceptions.Count > 0)
            {
                throw log;
            }
        }
    

    如果任务失败并且有一个“out List”变量,则返回 true 或 false。

        public bool TryDoTasks(MyTask[] taskList, out List<Exception> exceptions)
        {
            exceptions = new List<Exception>();
            foreach (MyTask task in taskList)
            {
                try
                {
                    DoTask(task);
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }
    
            if (exceptions.Count > 0)
            {
                return false;
            }
            else
            {
                exceptions = null;
                return true;
            }
        }
    

    【讨论】:

    • 我喜欢第二种解决方案,但我正在使用的项目到处都内置了“为违反业务规则而抛出异常”。这确实违反了这种模式。
    【解决方案5】:

    .NET 的 Task Parallel Library extensionswill become part of .NET 4.0)遵循其他答案中建议的模式:收集所有已被抛出到 AggregateException 类中的异常。

    通过总是抛出相同的类型(无论子工作有一个异常还是多个),处理异常的调用代码更容易编写。

    在 .NET 4.0 CTP 中,AggregateException 有一个公共构造函数(采用 IEnumerable&lt;Exception&gt;);对于您的应用程序来说,它可能是一个不错的选择。

    如果您的目标是 .NET 3.5,请考虑在您自己的代码中克隆您需要的 System.Threading.AggregateException 类部分,例如,一些构造函数和 InnerExceptions 属性。 (您可以将您的克隆放在程序集中的 System.Threading 命名空间中,如果您公开它可能会导致混淆,但以后升级到 4.0 会更容易。)当 .NET 4.0 发布时,您应该能够“通过从项目中删除包含克隆的源文件,将项目更改为以新框架版本为目标,然后重新构建,升级”到框架类型。当然,如果您这样做,您需要在 Microsoft 发布新的 CTP 时仔细跟踪此类的更改,以免您的代码变得不兼容。 (例如,这似乎是一个有用的通用类,他们可以将它从 System.Threading 移动到 System。)在最坏的情况下,您可以重命名该类型并将其移回您自己的命名空间(这使用大多数重构工具非常容易)。

    【讨论】:

    • 我多么希望我们已经拥有这个库。我一直在弄乱它,这太棒了。不幸的是,我们的目标是 .Net 3.5。
    • 其实你现在可以获取ParallelFx库了。 microsoft.com/downloads/…
    • 我不会将它投入生产。那是一个 CTP。
    • 我正在编写一个类似于 AggreateException 的类,可能会在 .Net 4.0 出来时替换它。
    • 还有一篇关于使用的 MSDN 杂志文章:msdn.microsoft.com/en-us/magazine/ee321571.aspx
    猜你喜欢
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 2010-09-10
    • 2014-01-10
    • 2018-12-24
    • 2018-12-21
    • 1970-01-01
    • 2012-02-11
    相关资源
    最近更新 更多