Бросать несколько исключений в .Net / C # - PullRequest
36 голосов
/ 10 ноября 2008

В приложении, над которым я работаю, любая ошибка бизнес-логики вызывает исключение, и вызывающий код обрабатывает исключение. Этот шаблон используется во всем приложении и работает хорошо.

У меня есть ситуация, когда я буду пытаться выполнить ряд бизнес-задач внутри бизнес-уровня. Для этого необходимо, чтобы сбой одной задачи не приводил к завершению процесса. Другие задачи все еще должны быть в состоянии выполнить. Другими словами, это не атомарная операция. Проблема, с которой я столкнулся, заключается в том, что в конце операции я хочу уведомить вызывающий код о том, что возникло исключение или исключения, вызвав исключение. Рассмотрим следующий фрагмент псевдо-кода:

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. Я не могу использовать никакие бета-библиотеки, или AggregateException в .Net 4.0, как упомянуто Брэдли Грейнджер (ниже), было бы хорошим решением для выброса исключений из коллекции.

Ответы [ 5 ]

38 голосов
/ 10 ноября 2008

Расширения параллельной библиотеки задач для .NET (которые станут частью .NET 4.0 ) следуют шаблону, предложенному в других ответах: сбор всех исключений, которые были брошены в AggregateException class.

Всегда выбрасывая один и тот же тип (независимо от того, есть ли одно исключение из дочерней работы или много), проще написать вызывающий код, который обрабатывает исключение.

В .NET 4.0 CTP, AggregateException имеет открытый конструктор (который принимает IEnumerable<Exception>); это может быть хорошим выбором для вашего приложения.

Если вы ориентируетесь на .NET 3.5, рассмотрите возможность клонирования частей класса System.Threading.AggregateException, которые вам нужны, в вашем собственном коде, например, некоторых конструкторов и свойства InnerExceptions. (Вы можете поместить свой клон в пространство имен System.Threading внутри вашей сборки, что может привести к путанице, если вы публично его представите, но позже упростит обновление до 4.0.) Когда выйдет .NET 4.0, вы сможете: обновить »до типа Framework, удалив исходный файл, содержащий ваш клон, из вашего проекта, изменив проект в соответствии с новой версией Framework и перестроив. Конечно, если вы сделаете это, вам нужно внимательно отслеживать изменения в этом классе, так как Microsoft выпускает новые CTP, чтобы ваш код не стал несовместимым. (Например, это кажется полезным классом общего назначения, и они могут переместить его с System.Threading на System.) В худшем случае вы можете просто переименовать тип и переместить его обратно в свое собственное пространство имен (это очень легко с большинством инструментов рефакторинга).

13 голосов
/ 10 ноября 2008

Два способа макушки моей головы - либо создать собственное исключение, либо добавить исключения к этому классу, и выбросить его в конец:

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

или верните true или false, если задачи не выполнены и есть переменная out out.

    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;
        }
    }
4 голосов
/ 10 ноября 2008

Вы можете создать пользовательское исключение, которое само по себе имеет коллекцию исключений. Затем в своем блоке Catch просто добавьте его в эту коллекцию. В конце вашего процесса проверьте, является ли количество исключений> 0, а затем сгенерируйте свое собственное исключение.

2 голосов
/ 10 ноября 2008

Возможно, вы захотите использовать BackgroundWorker , чтобы сделать это за вас. Он автоматически захватывает и представляет любые исключения после завершения, которые вы можете затем бросить или войти или сделать что-нибудь с. Кроме того, вы получаете преимущество многопоточности.

BackgroundWorker - приятная оболочка для модели асинхронного программирования делегата.

0 голосов
/ 10 ноября 2008

Здесь нет супер-элегантного решения, но есть несколько идей:

  • Передайте функцию обработчика ошибок в качестве аргумента DoTasks, чтобы пользователь мог решить, продолжать ли
  • Использовать трассировку для регистрации ошибок по мере их возникновения
  • Объединить сообщения из других исключений в сообщении пакета исключений
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...