Среда CLR вдоль и поперек

Обработка исключений поврежденного состояния

Эндрю Парду (Andrew Pardoe)

Эта статья основана на предварительной версии Visual Studio 2010. Любые содержащиеся в ней сведения могут быть изменены.

Содержание

Чем же являются исключения?
Системные исключения и SEH-исключения Win32
Управляемый код и SEH-исключения
Исключения поврежденного состояния
Использовать Catch (Exception e) все равно не следует
Пишите код благоразумно

Приходилось ли вам когда-нибудь писать такой код, который не вполне верен, но достаточно близок к правильному? Или такой, который нормально работает, когда все в порядке, но в котором нельзя быть уверенным, если что-то пойдет не так? Существует простой неверный оператор, который, вполне возможно, находится где-то в коде, который вам приходилось писать или сопровождать: catch (Exception e). Этот оператор кажется простым и понятным, но может причинить массу проблем, когда не делает то, что от него ожидается.

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

public void FileSave(String name)
{
    try
    {
      FileStream fs = new FileStream(name, FileMode.Create);
    }
    catch (Exception)
    {
      throw new System.IO.IOException("File Open Error!");
    }
}

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

Большинство людей обладает скорее рабочими навыками обработки исключений, чем глубоким пониманием процесса. Я начну с основных сведений, объясняющих обработку исключений со стороны среды CLR. Эти сведения могут оказаться полезными тем, кто, быть может, изучал обработку исключений по мере программирования или по старому институтскому учебнику. Опытные специалисты по обработке исключений в управляемом коде могут сразу переходить к разделу, посвященному различным видам исключений, или разделу, в котором приводится описание управляемого кода и исключений SEH (структурированная обработка исключений). В любом случае, несколько последних разделов стоит прочитать.

Чем же являются исключения?

Исключение – это сигнал, возникающий при обнаружении состояния, которое не ожидалось при нормальном выполнении потока программы. Многие агенты могут выявлять неверные условия и порождать исключения. Код программы (или используемой ею библиотеки) может вызывать исключения типов, производных от System.Exception; исключения может порождать среда CLR; исключения могут возникать и в неуправляемом коде. Исключения, возникшие в потоке исполнения, следуют за этим потоком сквозь машинный и управляемый код, через домены приложения. Если их не обрабатывает программа, то они рассматриваются операционной системой как необработанные исключения.

Исключение означает, что произошло нечто плохое. Хотя у каждого управляемого исключения есть свой тип (такой как System.ArgumentException или System.ArithmeticException), он имеет значение только в том контексте, в котором возникло исключение. Программа может обработать исключение, если понятны условия, которые его вызвали. Если программа не может обработать исключение, это может означать массу неприятностей. А после того как исключение покинуло программу, у него остается только одно значение, очень общее: случилось что-то плохое.

Когда Windows видит, что программа не обработала исключение, она пытается защитить сохраняемые данные программы (файлы на диске, параметры реестра и так далее), прервав процесс. Даже если изначально исключение указывало на некоторое неожиданное, но неопасное состояние программы (скажем, неудача извлечения из пустого стека), оно представляется серьезной проблемой для Windows, поскольку операционная система не обладает контекстом для правильной интерпретации исключения. Единственный поток в одном домене приложения, не обработав исключение, может вызвать сбой всего экземпляра среды CLR (см. рис. 1).

fig01.gif

Рис. 1. Необработанное исключение в одном потоке ведет к завершению всего процесса

Если исключения так опасны, почему они так популярны? Исключения подобны мотоциклам и бензопилам: они полезны благодаря своей грубой мощи. Обычный поток данных в потоке программы идет от функции к функции, через вызовы и возвраты. Каждый вызов функции создает кадр исполнения на стеке; каждое возвращение ликвидирует его. Помимо изменения глобального состояния, единственный поток данных в программе обеспечивается путем передачи данных между смежными кадрами в виде параметров функций или возвращаемых значений. В отсутствие обработки исключений каждой вызывающей функции необходимо проверять успешность выполнения вызванной функции (или просто предположить, что все всегда выполняется нормально).

Большинство вызовов Win32 API возвращают ненулевое значение для указания ошибки, поскольку Windows не использует обработку исключений. Программисту необходимо оборачивать каждый вызов функций в код, проверяющий возвращаемое значение вызванной функции. Например, следующий код из документации MSDN, который перечисляет файлы в каталоге, прямо проверяет успешность каждого вызова. Вызов FindNextFile(...) помещен в проверку, чтобы убедиться в том, что возвращается не ноль. Если вызов неудачен, то отдельный вызов функции – GetLastError() – предоставляет дополнительные сведения об исключительном состоянии. Обратите внимание, что каждый вызов в следующем кадре должен быть проверен на успешность, поскольку возвращаемые значения по необходимости ограничены областью видимости локальной функции:

// FindNextFile requires checking for success of each call 
while (FindNextFile(hFind, &ffd) != 0); 
dwError = GetLastError(); 
if (dwError != ERROR_NO_MORE_FILES) 
{ 
  ErrorHandler(TEXT("FindFirstFile")); 
} 
FindClose(hFind); 
return dwError; 

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

Затем система исключений очистит состояние каждого кадра на стеке вызова между тем местом, где возникло исключение, и тем, где оно будет обработано (второй проход). По мере очистки стека среда CLR будет выполнять блоки finally и fault в каждом кадре. Затем выполнится инструкция catch в обрабатывающем исключение кадре.

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

Дополнительные сведения о выдаче собственных типов исключений можно найти в статье журнала MSDN «Error Handling: Throwing Custom Exception Types from a Managed COM+ Server Application» (Выдача собственных типов исключений из управляемого серверного приложения COM+).

Системные исключения и SEH-исключения Win32

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

Если выполнение потока приводит к сбою процессора, то управление передается ядру операционной системы, которое представляет ошибку потоку как исключение SEH. Блок catch не знает, где на стеке потока возникло исключение, и ему не нужно знать, в какой именно точке ядро ОС создало исключение SEH.

Windows уведомляет потоки программ об исключениях ОС, используя SEH. Программисты, работающие с управляемым кодом, редко сталкиваются с ними, поскольку среда CLR обычно предотвращает типы ошибок, на которые указывают исключения SEH. Но если Windows создает исключение SEH, среда CLR доставит его управляемому коду. Хотя исключения SEH в управляемом коде редки, небезопасный управляемый код может вызвать исключение STATUS_ACCESS_VIOLATION, которое показывает, что программа попыталась получить доступ к неверному участку памяти.

Дополнительные сведения о SEH можно найти в статье Мэта Питрека (Matt Pietrek) "A Crash Course on the Depths of Win32 Structured Exception Handling" («Ускоренный курс по тонкостям структурированной обработки исключений») в номере журнала Microsoft Systems Journal за январь 1997 года.

Исключения SEH – это другой класс исключений, отличный от тех, что возникают в обычных программах. В программе может возникнуть исключение в случае попытки извлечения элемента из пустого стека или попытки открытия несуществующего файла. Все эти исключения имеют смысл в контексте исполнения программы. Исключения SEH ссылаются на контекст вне программы. Например, нарушение прав доступа (AV) указывает на попытку записи в память по недопустимому адресу. В отличие от ошибок программ, исключение SEH указывает, что могла быть скомпрометирована целостность процесса среды выполнения. Но хотя исключения SEH и отличаются от исключений, производных от System.Exception, когда среда CLR доставляет исключение SEH управляемому потоку, оно может быть перехвачено оператором catch (Exception e).

Некоторые системы пытаются разделить эти два рода исключений. Если программа скомпилирована с параметром /EH, то компилятор Microsoft Visual C++ различает исключения, порожденные оператором C++ throw, и исключения Win32 SEH. Это разделение полезно, поскольку обычная программа не знает, как обрабатывать ошибки, возникшие не по ее вине. Если программа C++ пытается добавить элемент к std::vector, она должна ожидать, что операция может потерпеть неудачу из-за недостатка памяти. Тем не менее, от правильной программы, использующей корректно написанные библиотеки, не следует ожидать разрешения проблемы с нарушением прав доступа.

Это разделение полезно для программистов. AV – это серьезная проблема: неожиданная запись в ключевую часть системной памяти может непредсказуемо повлиять на любую из частей процесса. Но некоторые ошибки SEH, такие как ошибка деления на ноль, вызванная неверным (и непроверенным) вводом пользователя, менее серьезны. Хотя программа с делением на ноль и неверна, ее ошибка вряд ли повлияет на другие части системы. Программа на С++, вероятно, сможет обработать ошибку деления на ноль, не дестабилизируя остальную часть системы. Так что хотя это разделение и полезно, оно не вполне выражает семантику, необходимую программистам управляемого кода.

Управляемый код и SEH

Исключения SEH всегда доставлялись средой CLR управляемому коду, используя ту же механику, что и для исключений, возникающих в самой программе. Это не проблема, пока код не пытается обработать исключительные условия, которые он не может обработать в должной мере. Большинство программ не могут безопасно продолжать работу после нарушения прав доступа. К сожалению, модель обработки исключений среды CLR подталкивала пользователей к перехвату этих серьезных ошибок путем предоставления программам права перехватывать любые исключения иерархии System.Exception. Но такая обработка ошибок редко бывает правильной.

Использование оператора catch (Exception e) — распространенная ошибка программистов, поскольку необработанные исключения приводят к серьезным последствиям. Можно возразить, что если неизвестно, какие ошибки могут возникнуть в функции, то при вызове этой функции необходимо защититься от всех возможных ошибок. Такой образ действий может показаться разумным, если не вспомнить, что он приведет к продолжению работы тогда, когда процесс, возможно, уже находится в поврежденном состоянии. Порой отмена и повторный запуск являются лучшим вариантом: никому не нравится видеть диалог «доктора Ватсона», но лучше перезапустить программу, чем повредить данные.

Программы, перехватывающие исключения из неизвестных им контекстов, являются серьезной проблемой. Но эту проблему нельзя решить, используя спецификации исключений или какой-либо еще механизм контракта. Не менее важно, чтобы управляемые программы могли получать уведомления об исключениях SEH, поскольку среда CLR является платформой для приложений различных видов. Некоторые из основных приложений, такие как SQL Server, нуждаются в полном контроле над процессами своих приложений. Управляемый код, взаимодействующий с машинным, порой сталкивается с исключениями C++ или SEH.

Но большинство программистов, пишущих catch (Exception e), на самом деле не желают перехватывать нарушения прав доступа. Они предпочли бы остановить исполнение своей программы при катастрофической ошибке, вместо того чтобы позволять ей ковылять дальше в неизвестном состоянии. Это особенно верно для таких программ, как Visual Studio или Microsoft Office, которые размещают управляемые надстройки. Если надстройка вызывает нарушение прав доступа и затем поглощает это исключение, то размещающая программа может повредить собственное состояние (или файлы пользователя), так и не обнаружив наличие ошибки.

В версии 4 среды CLR группа разработчиков продукта делает исключения, указывающие на повреждение состояния процесса, отличными от всех прочих исключений. Мы выделили около десяти исключений SEH, которые будут указывать на повреждение состояния процесса. Их обозначения относятся к контексту, в котором создается исключение, а не к самому типу исключения. Это значит, что нарушения прав доступа, полученные от Windows, будут помечены как исключения CSE (исключения поврежденного состояния), а исключения, возникающие в коде пользователя в результате порождения нового System.AccessViolationException, не будут помечены как CSE. Те, кто посетил конференцию PDC 2008, получили предварительную версию Visual Studio 2010, включающую в себя эти изменения.

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

Исключения поврежденного состояния

Начиная с версии 4 система исключений среды CLR не будет доставлять исключения CSE управляемому коду, если код не укажет явно, что он может обрабатывать исключения поврежденного состояния процесса. Это означает, что оператору catch (Exception e) в управляемом коде CSE передаваться не будут. Внесение этого изменения в систему обработки исключений среды CLR избавляет от необходимости изменять иерархию исключений или семантику обработки исключений любого из управляемых языков.

По соображениям совместимости группа разработчиков среды CLR предусмотрела некоторые способы выполнения старого кода со старым поведением:

  • Если требуется заново скомпилировать код, созданный в Microsoft .NET Framework 3.5, и выполнить его в .NET Framework 4.0, не обновляя при этом исходные файлы, то в файл конфигурации приложения можно добавить запись: legacyCorruptedStateExceptionsPolicy=true.
  • Сборки, скомпилированные в .NET Framework 3.5 или более ранних версиях среды выполнения, будут способны обрабатывать исключения поврежденного состояния (другими словами, поддерживать прежнее поведение) при работе на .NET Framework 4.0.

Если код должен обрабатывать исключения CSE, то функции, в которых содержатся операторы обработки исключений (catch, finally или fault), должны быть явно помечены новым атрибутом: System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions. Если возникнет исключение CSE, то среда CLR выполнит поиск подходящего оператора catch, но будет просматривать только функции, помеченные атрибутом HandleProcessCorruptedStateExceptions (см. рис. 2).

Рис. 2. Использование HandleProcessCorruptedStateExceptions

// This program runs as part of an automated test system so you need
// to prevent the normal Unhandled Exception behavior (Watson dialog).
// Instead, print out any exceptions and exit with an error code.
[HandledProcessCorruptedStateExceptions]
public static int Main()
{
    try
    {
        // Catch any exceptions leaking out of the program
        CallMainProgramLoop();
    }
    catch (Exception e) // We could be catching anything here
    {
        // The exception we caught could have been a program error
        // or something much more serious. Regardless, we know that
        // something is not right. We'll just output the exception
        // and exit with an error. We won't try to do any work when 
        // the program or process is in an unknown state!
        System.Console.WriteLine(e.Message);
        return 1;
    }
    return 0;
  } 

Если подходящий оператор catch найден, то среда CLR очистит стек как обычно, но будет исполнять блоки fault и finally (а в C# и неявный блок finally, создаваемый оператором using) только в функциях, помеченных этим атрибутом. В частично доверенном или прозрачном коде атрибут HandleProcessCorruptedStateExceptions игнорируется, поскольку полностью доверенной основной программе не нужен перехват и игнорирование этих серьезных исключений ненадежной надстройкой.

Использовать Catch (Exception e) все равно не следует

Хотя система исключений среды CLR и помечает самые опасные исключения как CSE, написание catch (Exception e) в коде остается плохой идеей. Исключения представляют целый спектр неожиданных ситуаций. Среда CLR может обнаруживать самые опасные исключения – исключения SEH, указывающие на возможное повреждение состояния процесса. Но другие неожиданные условия тоже могут быть вредоносны, если их игнорировать или обрабатывать в общем порядке.

Если процесс не поврежден, то среда CLR предлагает довольно надежные гарантии правильной работы программы и безопасности памяти. При исполнении программы, написанной в безопасном коде языка Microsoft Intermediate Language (MSIL), можно быть уверенным, что все инструкции в программе исполнятся верно. Но то, что написано в инструкциях программы, часто отличается от того, чего хотел программист. Совершенно правильно работающая с точки зрения среды CLR программа может повредить сохраненное состояние, скажем, записанные на диск файлы программы.

Рассмотрим простой пример программы, которая управляет базой данных школьных отметок. Эта программа использует принципы объектно-ориентированного проектирования для инкапсуляции данных и порождает управляемые исключения в случае неожиданных событий. В один прекрасный день секретарь лишний раз нажимает клавишу ВВОД при создании файла отметок. Программа пытается извлечь значение из пустой очереди и создает исключение QueueEmptyException, которое остается необработанным на стеке вызовов.

Где-то у вершины стека находится функция GenerateGrades() с оператором try/catch, которая перехватывает Exception. К сожалению, функция GenerateGrades() не знает, что данные об учащихся сохраняются в очереди, и не представляет, что делать с QueueEmptyException. Но программист, написавший GenerateGrades(), не хотел, чтобы программа завершала работу с ошибкой, не сохранив уже рассчитанных данных. Все безопасно записывается на диск, и работа программы прекращается.

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

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

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

Некоторые языки пытаются предотвратить перехват широкого класса исключений. Например, в C++ есть спецификации исключений; механизм, позволяющий программисту указывать, какие исключения могут возникнуть в функции. В языке Java сделан следующий шаг: проверяемые исключения этого языка требуют, чтобы был указан определенный класс исключений. Выполнение этого требования обеспечивается компилятором. В обоих языках исключения, которые могут выйти за пределы функции, перечисляются в объявлении этой функции, а от вызывающих функций требуется обработка этих исключений. Спецификация исключений – это хорошая идея, но применение ее на практике дает неоднозначные результаты.

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

Одна из таких ситуаций возникает, когда обработчик находится очень близко к месту, где возникло исключение. Например, рассмотрим программу, вызывающую машинный код, о котором известно, что в нем есть ошибки. В ходе отладки кода выясняется, что в нем есть указатель, который иногда обнуляется перед обращением по нему к памяти, что вызывает нарушение прав доступа. Может возникнуть желание использовать атрибут HandleProcessCorruptedStateExceptions для функции, которая вызывает машинный код с использованием P/Invoke, поскольку причина повреждения указателя известна и сохранение целостности процесса не вызывает опасения.

Другая ситуация, когда может потребоваться использование этого атрибута, возникает тогда, когда обработчик находится максимально далеко от ошибки. Программа практически готова завершить процесс. Допустим, что программист написал основное приложение или платформу, которая должна в случае ошибки выполнить заданную пользователем запись в журнал. Главную функцию можно поместить в блок try/catch/finally и пометить HandleProcessCorruptedStateExceptions. Если ошибка неожиданно достигнет главной функции программы, то та просто запишет некоторые данные в журнал, выполнив как можно меньше действий, и завершит процесс. Когда целостность процесса под угрозой, выполнение любых действий может быть опасно, но даже если запись в журнал закончится неудачей, иногда это допустимо.

Взгляните на схему, показанную на рис. 3. Здесь функция 1 (fn1()) помечена атрибутом [HandleProcessCorruptedStateExceptions], в результате чего оператор catch перехватывает нарушение прав доступа. Блок finally функции 3 не выполнится, даже если исключение будет перехвачено в функции 1. Функция 4, находящаяся внизу стека, создает нарушение прав доступа.

fig03.gif

Рис. 3. Исключение и нарушение прав доступа

Ни в одной из приведенных выше ситуаций полная безопасность выполняемых действий не гарантируется, но в некоторых случаях простое прерывание процесса недопустимо. С другой стороны, решение обрабатывать исключения CSE возлагает на программиста тяжкое бремя корректной работы с ними. Помните, что система исключений среды CLR не доставит исключение CSE ни одной функции, которая не помечена новым атрибутом, ни при первом проходе (когда выполняется поиск подходящего оператора catch), ни при втором проходе (когда очищается стек и выполняются блоки fault и finally).

Блок finally нужен именно для того, чтобы гарантировать выполнение кода вне зависимости от наличия или отсутствия исключения. (Блоки fault выполняются только тогда, когда происходит исключение, но их выполнение тоже гарантируется). Эти конструкции используются для очистки критических ресурсов, например, для освобождения дескрипторов файлов или для аннулирования контекстов олицетворения.

Даже в коде, надежность которого обеспечивается использованием областей ограниченного выполнения (CER), в случае возникновения CSE будут выполнены только функции, помеченные атрибутом HandleProcessCorruptedStateExceptions. Написать правильный код, который обрабатывает CSE и продолжает безопасно выполнять процесс, очень сложно.

Взгляните внимательнее на код, изображенный на рис. 4, чтобы увидеть, что может пойти не так. Если этот код находится не в функции, которая может обрабатывать CSE, то в случае нарушения прав доступа блок finally не будет выполнен. Это не страшно, если процесс прерывается – тогда открытый дескриптор файла будет освобожден. Но если какой-либо другой код перехватит нарушение прав доступа и попытается восстановить состояние, то этому коду должно быть известно о необходимости закрыть файл, а также восстановить любые другие внешние состояния, измененные программой.

Рис. 4. Блок finally может не выполниться

void ReadFile(int index)
    {
      System.IO.StreamReader file = 
        new System.IO.StreamReader(filepath);
          try
          {
            file.ReadBlock(buffer, index, buffer.Length);
          }
          catch (System.IO.IOException e)
          {
            Console.WriteLine("File Read Error!");
          }
          finally
          {
            if (file != null)
                {
                    file.Close()
                }
          }
    }

Код, которому предстоит обрабатывать CSE, должен ожидать наличия впечатляющей массы критических ресурсов, которые не были очищены. Блоки finally и fault не запускались. Области ограниченного выполнения не были выполнены. Программа — и процесс — находятся в неизвестном состоянии.

Если вы уверены, что код выполняет то, что от него требуется, то вам понятно, что нужно делать. Но вам неизвестно, в каком состоянии находится программа, то лучше просто завершить ее работу. Или, если приложение является размещаемым, то вызвать политику эскалации, указанную основным приложением. Для получения дополнительных рекомендаций по написанию надежного кода CER обратитесь к статье Алессандро Каторчини (Alessandro Catorcini) и Брайана Грюнкемайера (Brian Grunkemeyer) из выпуска рубрики «CLR вдоль и поперек» за декабрь 2007 года.

Кодируйте благоразумно

Хотя среда CLR и предотвращает простой перехват CSE, обработка слишком широких классов исключений на становится хорошей идеей. Но операторы вида catch (Exception e) часто появляются в коде, и это вряд ли изменится. Когда исключения, представляющие поврежденное состояние процесса, не доставляются коду, который просто перехватывает все исключения, это предотвращает ухудшение таким кодом и без того плохой ситуации.

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

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

Вопросы и комментарии направляйте по адресу clrinout@microsoft.com.

Эндрю Парду (Andrew Pardoe) – руководитель программы в группе разработчиков CLR корпорации Майкрософт. Он работает над многими аспектами исполнительных систем, как для Silverlight, так и для настольной среды. С ним можно связаться по адресу Andrew.Pardoe@microsoft.com.