Общее программирование путает в .NET при обрабатывании исключений? [закрытый]

Что такое некоторые наиболее распространенные ошибки, которые Вы видели сделанный при обрабатывании исключений?

Кажется, что обработка исключений может быть одной из самых твердых вещей изучить, как сделать правильно в .NET. Особенно рассмотрение ответа в настоящее время № 1 на Общее программирование принимает за разработчиков.NET для предотвращения? связан с обработкой исключений.

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

38
задан 2 revs 23 May 2017 в 12:07
поделиться

14 ответов

Какие наиболее распространенные ошибки вы видели при обработке исключений?

Я могу вспомнить много.

Сначала прочитайте мою статью о классификации исключений на досадные, костоломные, фатальные и экзогенные:

http://ericlippert.com/2008/09/10/vexing-exceptions/

Некоторые распространенные ошибки:

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

  • Ошибка безопасности: сбой в небезопасном режиме

    try
    {
     result = CheckPassword();
     if (result == BadPassword) throw BadPasswordException();
    }
    catch(BadPasswordException ex) { ReportError(ex); return; }
    catch(Exception ex) { LogException(ex); }
    AccessUserData();
    

    Видите, что произошло? Мы перешли в небезопасный режим. Если CheckPassword выбросил NetworkDriverIsAllMessedUpException, то мы поймали его, записали в журнал и получили доступ к данным пользователя независимо от того, был ли пароль правильным. Перейдите в безопасный режим; когда вы получаете любое исключение, предполагайте худшее.

  • Ошибка безопасности: производство исключений, которые утекают конфиденциальную информацию, прямо или косвенно.

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

    Забавная история. Перед тем, как .NET 1.0 был отправлен клиентам, мы обнаружили ошибку, когда можно было вызвать метод, который выбрасывал исключение "сборка, вызвавшая этот метод, не имеет разрешения на определение имени файла C:\foo.txt". Отлично. Спасибо, что сообщили мне об этом. Что мешает этой сборке перехватить исключение и опросить его сообщение, чтобы узнать имя файла? Ничего. Мы исправили это перед отправкой.

    Это прямая проблема. Косвенной проблемой может быть проблема, которую я реализовал в LoadPicture, на VBScript. Он выдавал разное сообщение об ошибке в зависимости от того, является ли неверный аргумент каталогом, файлом, не являющимся изображением, или несуществующим файлом. Это означает, что вы можете использовать его как очень медленный дисковый браузер! Пробуя кучу разных вещей, можно постепенно составить представление о том, какие файлы и каталоги находятся на чьем-то жестком диске. Исключения должны быть разработаны таким образом, чтобы, если они обрабатываются ненадежным кодом, этот код не узнал ничего из личной информации пользователя о том, что он сделал, чтобы вызвать исключение. (LoadPicture теперь выдает гораздо менее полезные сообщения об ошибках.)

  • Ошибки безопасности и управления ресурсами: Обработчики, которые не очищают ресурсы, являются утечками ресурсов, которые только того и ждут. Утечки ресурсов могут быть использованы в качестве атак типа "отказ в обслуживании" враждебным кодом частичного доверия, который намеренно создает ситуации, вызывающие исключения.

  • Ошибка устойчивости: Обработчики должны считать, что состояние программы испорчено, если они не обрабатывают конкретное внешнее исключение. Это особенно верно для блоков finally. Когда вы обрабатываете неожиданное исключение, вполне возможно и даже вероятно, что в вашей программе что-то сильно испорчено. Вы понятия не имеете, работает ли какая-либо из ваших подсистем, а если работает, то улучшит ли ее вызов ситуацию или ухудшит. Сосредоточьтесь на регистрации ошибки и сохранении пользовательских данных, если это возможно, и завершите работу настолько чисто, насколько это возможно. Предположим, что ничего не работает правильно.

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

http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx

44
ответ дан 27 November 2019 в 03:05
поделиться

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

0
ответ дан 27 November 2019 в 03:05
поделиться

Неправильно

try
{
   // Do something stupid
}
catch
{
   // ignore the resulting error because I'm lazy, and later spend
   // a week trying to figure out why my app is crashing all over
   // the place.
}

Лучше

try
{
    /// do something silly.
}
catch (InvalidOperationException ex)
{
    /// respond, or log it.
}
catch (Exception e)
{
    /// log it.
}
0
ответ дан 27 November 2019 в 03:05
поделиться

Повторное генерирование исключения с бессмысленным сообщением.

try
{
    ...
}
catch (Exception ex)
{
   throw new Exception("An error ocurred when saving database changes").
}

Вы не поверите, как часто я вижу, что подобный код работает в продакшене.

10
ответ дан 27 November 2019 в 03:05
поделиться

Забудьте устанавливать внутреннее исключение при повторном создании перехваченное исключение

try
{
    ...
}
catch (IOException ioException)
{
    throw new AppSpecificException("It was not possible to save exportation file.")
    // instead of
    throw new AppSpecificException("It was not possible to save exportation file.", ioException);
}

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

6
ответ дан 27 November 2019 в 03:05
поделиться

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

Другой пример:

try
{
     Insert(data);
}
catch (SqlException e)
{
   //oh this is a duplicate row, lets change to update
   Update(data);
}
3
ответ дан 27 November 2019 в 03:05
поделиться

Повторное генерирование исключений вроде этого:

try 
{ 
   // some code here
}
catch(Exception ex)
{
   // logging, etc
   throw ex;
}

Это убивает трассировку стека, делая гораздо менее полезными. Правильный способ повторного запуска будет следующим:

try 
{ 
   // some code here
}
catch(Exception ex)
{
   // logging, etc
   throw;
}
25
ответ дан 27 November 2019 в 03:05
поделиться

Невозможность перехвата возможных исключений внутри обработчика catch. Это может привести к распространению неправильного исключения вверх.

Например:

try
{
    DoImportantWork();
}
catch
{
    Cleanup();        
    throw;
}

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

try
{
    DoImportantWork();
}
catch
{
    try
    {
        Cleanup();        
    }
    catch
    {
        // We did our best to clean up, and even that failed.
        // If you try to log this error, the logging may throw yet another Exception.
    }
    throw;
}
1
ответ дан 27 November 2019 в 03:05
поделиться

Неиспользование using на IDisposable объектах:

File myFile = File.Open("some file");
callSomeMethodWhichThrowsException(myFile);
myFile.Close();

myFile не закрывается до вызова финализатора myFile (что может быть никогда), потому что исключение было брошено до вызова myFile.Close().

Правильный способ сделать это -

using(File myFile = File.Open("some file"))
{
    callSomeMethodWhichThrowsException(myFile);
}

Это переводится компилятором в нечто вроде:

File myFile = File.Open("some file");
try
{
    callSomeMethodWhichThrowsException(myFile);
}
finally
{
    if(myFile != null)
        myFile.Dispose(); //Dispose() calls Close()
}

Таким образом, файл закрывается даже перед лицом исключений.

7
ответ дан 27 November 2019 в 03:05
поделиться

Для регистрации Exception.Message вместо Exception.ToString ()

Часто я вижу, как код регистрирует только сообщение об исключении, в то время как он должен регистрировать возврат метода ToString. ToString предоставляет гораздо больше информации об исключении, чем сообщение. Помимо сообщения, он включает такую ​​информацию, как внутреннее исключение и трассировку стека.

3
ответ дан 27 November 2019 в 03:05
поделиться

Никто не говорит о том, чтобы видеть такие пустые блоки catch, как эти ....

 try{  
      //do something
    }
catch(SQLException sqex){  
        // do nothing  
    }

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

 try{  
     //do something  

 }catch(SQLException sqex){  

     //do something else  
 }
9
ответ дан 27 November 2019 в 03:05
поделиться

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

try {
  // Do something.
} catch (Exception exc) {
  // Do something.
}

Вместо:

try {
  // Do something.
} catch (IOException exc) {
  // Do something.
}

Исключения следует упорядочивать от наиболее конкретных к наименее важным.

12
ответ дан 27 November 2019 в 03:05
поделиться

Уловитель пустого:

//What's the point?
catch()
{}

Перебрасывание:

//Exceptions are for *adding* detail up the stack
catch (Exception ex)
{throw ex;}
4
ответ дан 27 November 2019 в 03:05
поделиться

Попытка поймать OutOfMemoryException или StackOverflowException - они приводят к завершению работы среды выполнения и, следовательно, к способу их перехвата из одного процесса ( или даже из среды CLR в целом?)

OutOfMemoryException: исключение, которое выдается, когда недостаточно памяти для продолжения выполнения программы.

«Начиная с .NET Framework версии 2.0, объект StackOverflowException не может быть перехвачен блоком try-catch, и соответствующий процесс завершается по умолчанию. Следовательно, пользователям рекомендуется писать свой код для обнаружения и предотвращения переполнения стека. . "

1
ответ дан 27 November 2019 в 03:05
поделиться