Какое преимущество предоставляет новая функция "Фильтр исключений"?

С# 6 имеет новую функцию, называемую фильтрацией исключений

Синтаксис выглядит следующим образом:

catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
{  
    //Do something here 
}  

Я не мог не задаться вопросом, какова польза от нынешнего подхода:

catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
}

Разве дело в том, что фильтрация происходит до фигурной скобки? Возможно, в отношении производительности или безопасности?

Ответ 1

Функция фильтра исключений в С# 6.0 предоставляет различные преимущества. Здесь объяснение некоторых (упорядоченных по моей воспринимаемой важности)

  • Параметр четности. Исключительные фильтры уже были реализованы на уровне IL, а другие .Net-языки (VB.Net и F #) [1] и как часть создания нового компилятора для С# и VB.Net(проект "Roslyn" ) реализовано множество функций, существующих на одном языке и отсутствующих в другом. [2].

  • Crash Dumps. Исключительные фильтры не изменяют стек. это означает, что если он будет сброшен (в дампе сбоя), вы сможете узнать, откуда было изначально выбрано исключение, и не только там, где оно было сброшено (что не имеет отношения к реальной проблеме) [3]

  • Отладка. Когда исключение входит в блок catch, заново используется с помощью throw; и не обрабатывается нигде в стеке (и настройки исключения устанавливаются для прерывания, когда исключение не обрабатывается пользователем) отладчик будет разбиваться на throw; вместо того, чтобы изначально было выбрано исключение (т.е. в приведенном ниже примере он разбился бы на throw;, а не throw new FileNotFoundException();)

  • Несколько catch Блоки. Без фильтров исключений вы должны поймать исключение, проверить условие и если оно не выполнено throw; исключение. Исключенное исключение не рассматривает другие блоки catch, даже если исключение удовлетворяет всем условиям, которые в конечном итоге приводят к одному большому блоку catch

    try
    {
        throw new FileNotFoundException();
    }
    catch (FileNotFoundException e)
    {
        if (!e.FileName.Contains("Hamster"))
        {
            throw;
        }
        // Handle
    }
    catch (Exception e)
    {
        // Unreachable code
    }
    
  • Считываемость. Хотя вы могли бы использовать блок "catch all" catch со многими условиями и throw;, когда они не выполняются (при переносе изменений в стек), это гораздо четче иметь отдельные и различные блоки catch, где каждый обрабатывает определенную проблему соответствующим образом:

    try
    {
    }
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
    { 
    }  
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00011)  
    {  
    }
    catch (IOException)
    {  
    }
    
  • "Злоупотребление" . Вы можете использовать фильтры исключений в качестве способа проверки исключения без его обработки. Хотя это не главное преимущество, это хороший побочный эффект. У вас может быть ложный возвращаемый метод ведения журнала и пустой блок catch:

    private static bool LogException(Exception exception)
    {
        Console.WriteLine(exception.Message);
        return false;
    }
    
    try
    {
    }
    catch (ArgumentException exception) when (LogException(exception))  
    {
        // Unreachable code.
    }
    

В заключение, большинство функций С# 6.0 - это небольшие улучшения и синтаксический сахар, а в то время как фильтры исключений - не очень большая функция, она предоставляет функциональность, которая была невозможна раньше.


  • Предварительный просмотр языка С# 6.0

    Другое исключение в С# 6.0 для поддержки фильтров исключений - обновляет язык с другими языками .NET, а именно Visual Basic.NET и F #

  • Функции языков в С# 6 и VB 14

  • Новые функции на С# 6

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

Ответ 2

Эрен Эрсёнмез уже упомянул о наиболее важной пользе.

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

С другой стороны, фильтры исключений не разматывают стек, поэтому, если исключение не поймано, отладчик покажет место, где первоначально было выбрано исключение.

Итак, если у вас есть это:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
     else
     {
         throw;
     }
}

отладчик остановится на throw, если NativeErrorCode не 0x42.

Но если у вас есть это:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception) if (exception.NativeErrorCode == 0x00042)
{         
     //Do something here 
}

отладчик остановится в DoSomethingThatThrows (или в вызванном им методе), позволяя вам легче видеть причину ошибки (опять же, когда NativeErrorCode не является 0x42)

Ответ 3

Из официальных описаний функций С# (ссылка для загрузки PDF, как и на просмотр VS2015):

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

Это также распространенная и принятая форма "злоупотребления" для использования исключения фильтры для побочных эффектов; например Ведение журнала. Они могут проверять исключение "летать", не перехватывая его ход. В этих случаях фильтр часто является вызовом функции возврата с ложным возвратом, которая выполняет побочные эффекты:

private static bool Log(Exception e) { /* log it */ ; return false; }
…
try { … } catch (Exception e) if (Log(e)) {}

Ответ 4

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

Ответ 5

Настоящая выгода ИМО:

try
{
}
catch (SomeException ex) if (ex.Something)
{
}
catch (Exception ex)
{
  // SomeException with !ex.Something is caught here
  // not the same as calling `throw` for !ex.Something 
  // without filters in previous handler
  // less code duplication
}