简体   繁体   English

在.Net / C#中抛出多个异常

[英]Throwing multiple exceptions in .Net/C#

In an application I work on, any business logic error causes an exception to be thrown, and the calling code handles the exception. 在我处理的应用程序中,任何业务逻辑错误都会导致抛出异常,并且调用代码会处理异常。 This pattern is used throughout the application and works well. 这种模式在整个应用程序中使用并且运行良好。

I have a situation where I will be attempting to execute a number of business tasks from inside the business layer. 我有一种情况,我将尝试从业务层内部执行许多业务任务。 The requirement for this is that a failure of one task should not cause the process to terminate. 对此的要求是一个任务的失败不应导致进程终止。 Other tasks should still be able to execute. 其他任务仍然可以执行。 In other words, this is not an atomic operation. 换句话说,这不是原子操作。 The problem I have is that at the end of the operation, I wish to notify the calling code that an exception or exceptions did occur by throwing an exception. 我遇到的问题是,在操作结束时,我希望通过抛出异常来通知调用代码确实发生了异常。 Consider the following psuedo-code snippet: 请考虑以下psuedo-code代码段:

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
}

What do I throw? 我该扔什么? I have encountered this pattern before in my career. 我在职业生涯之前遇到过这种模式。 In the past I have kept a list of all exceptions, then thrown an exception that contains all the caught exceptions. 在过去,我保留了所有异常的列表,然后抛出包含所有捕获的异常的异常。 This doesn't seem like the most elegant approach. 这似乎不是最优雅的方法。 Its important to preserve as many details as possible from each exception to present to the calling code. 重要的是从每个异常中保留尽可能多的细节以呈现给调用代码。

Thoughts? 思考?


Edit: The solution must be written in .Net 3.5. 编辑:解决方案必须用.Net 3.5编写。 I cannot use any beta libraries, or the AggregateException in .Net 4.0 as mentioned by Bradley Grainger (below) would be a nice solution for collection exceptions to throw. 我不能使用任何beta库,或Bradley Grainger (下面)提到的.Net 4.0中的AggregateException对于抛出的集合异常将是一个很好的解决方案。

The Task Parallel Library extensions for .NET (which will become part of .NET 4.0 ) follow the pattern suggested in other answers: collecting all exceptions that have been thrown into an AggregateException class. .NET的任务并行库扩展将成为.NET 4.0的一部分 )遵循其他答案中建议的模式:收集已抛入AggregateException类的所有异常。

By always throwing the same type (whether there is one exception from the child work, or many), the calling code that handles the exception is easier to write. 通过始终抛出相同的类型(子工作中是否有一个例外,或许多),处理异常的调用代码更容易编写。

In the .NET 4.0 CTP, AggregateException has a public constructor (that takes IEnumerable<Exception> ); 在.NET 4.0 CTP中, AggregateException有一个公共构造函数(它接受IEnumerable<Exception> ); it may be a good choice for your application. 它可能是您的应用程序的一个很好的选择。

If you're targeting .NET 3.5, consider cloning the parts of the System.Threading.AggregateException class that you need in your own code, eg, some of the constructors and the InnerExceptions property. 如果您的目标是.NET 3.5,请考虑克隆您自己的代码中所需的System.Threading.AggregateException类的部分,例如,一些构造函数和InnerExceptions属性。 (You can place your clone in the System.Threading namespace inside your assembly, which could cause confusion if you exposed it publicly, but will make upgrading to 4.0 easier later on.) When .NET 4.0 is released, you should be able to “upgrade” to the Framework type by deleting the source file containing your clone from your project, changing the project to target the new framework version, and rebuilding. (您可以将克隆放置在程序集内的System.Threading命名空间中,如果您公开公开它可能会导致混淆,但稍后会更容易升级到4.0。)当.NET 4.0发布时,您应该能够“通过从项目中删除包含克隆的源文件,更改项目以定位新的框架版本并重建,从而“升级到Framework类型”。 Of course, if you do this, you need to carefully track changes to this class as Microsoft releases new CTPs, so that your code doesn't become incompatible. 当然,如果你这样做,你需要仔细跟踪这个类的更改,因为Microsoft发布了新的CTP,这样你的代码就不会变得不兼容。 (For example, this seems like a useful general-purpose class, and they could move it from System.Threading to System .) In the worst case, you can just rename the type and move it back into your own namespace (this is very easy with most refactoring tools). (例如,这似乎是一个有用的通用类,它们可以将它从System.Threading移动到System 。)在最坏的情况下,您可以重命名该类型并将其移回您自己的命名空间(这是非常的使用大多数重构工具都很容易)。

Two ways of the top of my head would be either make a custom exception and add the exceptions to this class and throw that the end : 我头脑中的两种方法是创建一个自定义异常并将异常添加到此类并抛出结束:

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;
        }
    }

or return true or false if the tasks failed and have a 'out List' variable. 如果任务失败并且有一个'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;
        }
    }

You could create a custom Exception that itself has a collection of Exceptions. 您可以创建一个自定义的Exception,它本身有一组Exceptions。 Then, in your Catch block, just add it to that collection. 然后,在您的Catch块中,只需将其添加到该集合中。 At the end of your process, check if the Exception count is > 0, then throw your custom Exception. 在进程结束时,检查Exception计数是否> 0,然后抛出自定义异常。

You might want to use a BackgroundWorker to do this for you. 您可能希望使用BackgroundWorker为您执行此操作。 It automatically captures and presents any exceptions when completed, which you could then throw or log or do whatever with. 它会在完成时自动捕获并显示任何异常,然后您可以抛出或记录或执行任何操作。 Also, you get the benefit of multithreading. 此外,您还可以获得多线程的好处。

The BackgroundWorker is a nice wrapper around delegate's asynchronous programming model. BackgroundWorker是委托异步编程模型的一个很好的包装器

No super-elegant solution here but a few ideas: 这里没有超级优雅的解决方案,但有一些想法:

  • Pass an error-handler function as argument to DoTasks so the user can decide whether to continue 将错误处理函数作为参数传递给DoTasks,以便用户可以决定是否继续
  • Use tracing to log errors as they occur 使用跟踪记录错误
  • Concatenate the messages from the other exceptions in the exception bundle's message 连接来自异常包的消息中的其他异常的消息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM