Подтвердить что ты не робот

Общие ошибки программирования в .Net при обработке исключений?

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

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

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

4b9b3361

Ответ 1

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

Я могу много думать.

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

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 теперь дает гораздо менее полезные сообщения об ошибках.)

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Вместо

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

Исключения должны быть упорядочены от наиболее специфического до наименьшего.

Ответ 4

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

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

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

Ответ 5

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

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

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

 try{  
     //do something  

 }catch(SQLException sqex){  

     //do something else  
 }

Ответ 6

Не использовать 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

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

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

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

Ответ 8

Пустой улов:

//What the point?
catch()
{}

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

//Exceptions are for *adding* detail up the stack
catch (Exception ex)
{throw ex;}

Ответ 9

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

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

try
{
     Insert(data);
}
catch (SqlException e)
{
   //oh this is a duplicate row, lets change to update
   Update(data);
}

Ответ 10

Записать Exception.Message вместо Exception.ToString()

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

Ответ 11

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

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

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

Ответ 12

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

Например:

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

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

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

Ответ 13

Wrong

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

Ответ 14

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