Можете ли вы поймать более одного типа исключения с каждым блоком?

Этот вопрос близок к тому, что я хочу сделать, но не совсем там.

Есть ли способ упростить следующий код?

private bool ValidDirectory(string directory)
{
    if (!Directory.Exists(directory))
    {
        if (MessageBox.Show(directory + " does not exist. Do you wish to create it?", this.Text) 
            == DialogResult.OK)
        {
            try
            {
                Directory.CreateDirectory(directory);
                return true;
            }
            catch (IOException ex)
            {
                lblBpsError.Text = ex.Message;
            }
            catch (UnauthorizedAccessException ex)
            {
                lblBpsError.Text = ex.Message;
            }
            catch (PathTooLongException ex)
            {
                lblBpsError.Text = ex.Message;
            }
            catch (DirectoryNotFoundException ex)
            {
                lblBpsError.Text = ex.Message;
            }
            catch (NotSupportedException ex)
            {
                lblBpsError.Text = ex.Message;
            }
        }
    }

    return false;
}

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

Я просто пытаюсь быть (тоже) ленивым?

Ответы

Ответ 1

Вы можете использовать:

catch (SystemException ex)
{
  if(    (ex is IOException)
      || (ex is UnauthorizedAccessException )
// These are redundant
//    || (ex is PathTooLongException )
//    || (ex is DirectoryNotFoundException )
      || (ex is NotSupportedException )
     )
       lblBpsError.Text = ex.Message;
    else
        throw;
}

Ответ 2

Если исключения имеют общий суперкласс, вы можете просто перехватить суперкласс.

Ответ 3

Да, вы пытаетесь быть ленивым, но лень является одним из достоинства программиста, так что хорошо.

Что касается вашего вопроса: я не знаю, о чем я знаю, но есть некоторые обходные пути:

  • Предоставьте Исключениям общий предок. Я думаю, что это невозможно в вашем случае, поскольку они, похоже, встроены.
  • Вы можете получить наиболее общее исключение.
  • Переместите код обработки в его собственную функцию и вызовите это из каждого блока catch.

Ответ 4

Это раздражает, и другие ответы предложили хорошие обходные пути (я бы использовал @Lotfi's).

Однако это поведение является требованием, предъявляемым к типу безопасности С#.

Предположим, вы могли бы сделать это:

try
{
    Directory.CreateDirectory(directory);
    return true;
}
catch (IOException, 
    UnauthorizedAccessException,
    PathTooLongException,
    DirectoryNotFoundException,
    NotSupportedException ex)
{
    lblBpsError.Text = ex.Message;
}

Теперь какой тип ex? Все они имеют .Message, потому что они наследуют System.Exception, но пытаются получить доступ к любому из своих других свойств, и у вас есть проблема.

Ответ 5

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

Ответ 6

Просто ради полноты:

В VB вы можете использовать условную обработку исключений:

Try
    …
Catch ex As Exception When TypeOf ex Is MyException OrElse _
                           TypeOf ex Is AnotherExecption
    …
End Try

Такой блок Catch будет введен только для указанных исключений - в отличие от С#.

Возможно, будущая версия С# предложит подобную функцию (в конце концов, для этого кода есть определенная команда IL).

MSDN: Как фильтровать ошибки в блоке Catch в Visual Basic

Ответ 8

Вы можете поймать исключение базового класса (все ваши исключения происходят из SystemException):

try
{
  Directory.CreateDirectory(directory);
  return true;
}
catch (SystemException ex)
{
  lblBpsError.Text = ex.Message;
}

Но тогда вы можете поймать исключения, которые вы не хотите ловить.

Ответ 9

Вы можете использовать делегатов, это будет делать то, что вы хотите:

EDIT: упрощен бит

static void Main(string[] args)
{
    TryCatch(() => { throw new NullReferenceException(); }, 
        new [] { typeof(AbandonedMutexException), typeof(ArgumentException), typeof(NullReferenceException) },
        ex => Console.WriteLine(ex.Message));

}

public static void TryCatch(Action action, Type[] exceptions, Action<Exception> catchBlock)
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
         if(exceptions.Any(p => ex.GetType() == p))
         {
             catchBlock(ex);
         }
         else
         {
             throw;
         }
    }
}

Ваша конкретная попытка /catch будет:

bool ret;
TryCatch(
    () =>
        {
            Directory.CreateDirectory(directory);
            ret = true;
        },
    new[]
        {
            typeof (IOException), typeof (UnauthorizedAccessException), typeof (PathTooLongException),
            typeof (DirectoryNotFoundException), typeof (NotSupportedException)
        },
    ex => lblBpsError.Text = ex.Message
);

return ret;

Ответ 10

Я понимаю, что некоторые из этих исключений не могут быть предсказуемыми, но, где возможно, попытайтесь реализовать свою собственную "упреждающую" логику. Исключения являются дорогостоящими, хотя в этом случае, вероятно, не является выключателем сделки.

Например, используйте Directory.GetAccessControl(...) вместо того, чтобы полагаться на исключение UnauthorizedAccessException.

Ответ 11

Посмотрите Блок приложений для обработки исключений из EntLib. Они формулируют очень хорошую методологию обработки исключений на основе политики и конфигурации, которая позволяет избежать больших блоков условной логики.