Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 52
Текст из файла (страница 52)
1 и пе гехсер 01оп помогает смягчить проблему, но все равно трудно найти изначальную причину проблемы, если исключения транслируются по пути. Исключения, сгенерированные в блоке йхпа11у Возможно, но крайне нежелательно. генерировать исключения внутри блока 11па11у. Следующий код демонстрирует пример: ия1пд Яуягеш) из1пд Яуясеш.со11ес11опял риЬ11с с1аяя Епсгуроупп ( ясас1с чотб Магп() ( сгу ( игу АггауЬгзп 1гяг = пен АггауЬ1ят()) 11яс.лсЫ( 1 )) Сопяо1е.иг1сеЬЬпе( "Элемент 10 = (О)", 1гяс(10) ): ) 11па11у ( Сопяо1е.нг1сеЬЬпе( "Очистка..." ); Сплин лен Ехсерпуоп( "Лучше сгенерировать исключение" )) ) ) савсп( Агдишепсбитбраапдевхсерсаоп ) ( Сопяо1е.иг1геЬЬпе( "Аргумент вышел эа допустимые пределы!" ) салоп ( Сопяо1е.иг11еЬЬпе( "Готово" )) Безопасность и обработка исключений 197 Первое исключение, АгдпшепсбисОЖеапдеехсерс1оп, просто теряется, а новое исключение распространяется по стеку.
Ясно, что это нежелательно. Никогда не теряйте след исключений, поскольку тогда практически невозможно определить, что именно вызвало исключение в самом начале. Исключения, сгенерированные в финализаторах В действительности деструкторы Са являются не детерминированными деструкторами, а финализаторами СЬК. Финалиэаторы исполняются в контексте потока финализатора, который на самом деле является контекстом произвольного потока. Если финализатор должен генерировать исключение, то СЬК может не знать, как обработать зту ситуацию, и просто прервет поток (и процесс).
Рассмотрим следующий код: паапа Яуясеш) риЫТс с1аяя Регяоп ( -Регяоп () ( Оопяо1е.иг1гепапе( "Очистка Регяоп..." Оопяо1е.нг1ге11пе( "Очистка Регяоп завершена..." ) ) роЬ11с с1аяя Ешр1оуее: Регяоп -Ешр1оуее() ( Оопяо1е.иг1сеЫпе( "Очистка Ешр1оуее..." )) оЬЯесп оЬ1 = по11; /1 Оледуюший код сгенерирует исключение. Оопао1е.нг1кепапе( оЬз.тоЯСгапс() Оопяо1е.нгукетапе( "Очистка Ешр1оуее завершена..." ) роЫТс с1аяя Епкгуроапп ягагус чо1б Ма1п() ( Ешр1оуее ешр =. пеи Ешр1оуее()) еп1р = пп11; Ниже показан вывод, полученный в результате выполнения этого кода: Очистка Ешр1оуее...
Опьапс1еб ехсерс1оп: яуясеш.мп11песегепсеехсерс1оп: Оьйесс гетегепсе пос яес Со ап Тпякапсе от ап оьзесг. ап Ешр1оуее.Р1па11яе() Необработанное исключение: Яуяяеш.ши11негегеосеЕхсергаол: Объектная ссылка не установлена а якяемпллр объекта. ая Ешр1оуее.ркпа11яе П Очистка Регяоп... Очистка Регяоп завершена. При запуске этого примера в средах .)))ЕТ 1. 1, .)))ЕТ 2.0 и последующих версий обнаружится небольшие отличия в поведении. В )ЧЕТ 1. 1 исключение "проглатывается" вместе с выводом на консоль, и выполнение продолжается дальше. Начиная с версии .)))ЕТ 2.0, исполняющая среда отобразит знакомое диалоговое окно отладки ЛТ с запросом о необходимости проведения отладки приложения.
Проблема в том, что на ответ вьщеляется 198 Глава 7 мало времени, и работа приложения прекращается. Ранее в разделе "Изменения, касающиеся необработанных исключений, которые появились в .)))ЕТ 2.0" были описаны различия в трактовке необработанных исключений в .)дЕТ 1. 1, .)))ЕТ 2.0 и последующих версий среды. Нужно любой ценой избегать намеренной генерации исключений в финализаторах, поскольку тем самым можно прервать процесс. Кроме того, внимательно изучите все приводимые в главе 13 аргументы "за" и "против", касающиеся создания финализаторов.
Исключения, сгенерированные в статических конструкторах Если исключение сгенерировано, а в стеке нет обработчика, поэтому его поиск завершается в статическом конструкторе типа, то исполняющая система обрабатывает этот случай специальным образом. Она транслирует исключение в Яуягею. Туре1п1Г1а11гэг1опЕхсерг1оп и генерирует его взамен первоначального. Перед генерацией нового исключения свойство ЬппегЕхсерг1оп экземпляра Туре1паггэ11гэгуопЕхсерг1оп устанавливается в исходное исключение. Таким образом, любой обработчик исключения инициализации типа может легко обнаружить причину сбоя, Такая трансляция исключения имеет смысл. потому что конструкторы по своей природе не могут возвращать значение, свидетельствующее об успехе или неудаче. Исключения — единственный доступный механизм, который позволяет сигнализировать о сбое конструктора. Что более важно; поскольку система вызывает статические конструкторы в определенное самой системой время'.
для ннх имеет смысл применять тип Туре1п1г ьэ11гаг1опЕхсерг1оп, который позволит точнее определить, когда чтото идет не так. Например, предположим, что имеется статический конструктор, который может потенциально генерировать исключение АгдпюепгОиГОЙЕапдеЕхсерг1оп, Теперь представьте разочарование пользователей, если исключение распространится во включающем потоке в некоторый случайный момент времени, поскольку точный момент вызова статического конструктора определяется системой. Может показаться. что АгдпюепгОпгОТЕапдекхсерг1оп материализуется буквально из ничего. Помещение исключения в оболочку Туре1п111з11гэгаопЕхсерг1оп немного проясняет ситуацию и предупреждает пользователей, или, надо надеяться, разработчика, о том.
что проблема возникла во время инициализации типа. В приведенном ниже коде демонстрируется пример того, как выглядит туре1п1ЬЬэ11гасгопехсерг1ол с вложенным внутри него исключением: ия1пд Яуягею) пя1пд Яувгею.10) с1авв Ечепгьоддег ( вгэгас ЕчепгЬоддег() ( ечепгьод = Е11е.СгеагеТехг( "1од111е.гхг" (! Следуююий оператор сгенерирует исключение. яггЬоднаюе = (ягг1пд) яггЬодкэюе.С1опе(); ) вгаг1с рпЬЬЬс кора Кг1геЬод( ягг1пд яоиэтехг ) ( ечепгЬод.кг1ге( воиеТехг ) Система может вызывать статические конструкторы во время загрузки типа нли просто перед обращением к статическому члену, в зависимости от того, как сконфигурирована СЬВ длн текущего процесса.
Безопасность и обработка исключений 199 зТаТЬс Ргачаге БсгеаюигЬТег ечепТЬодт зТаТЬс Рг1чаТе зггапд зсгЬодизюе; ) Рцбаас с1дзз Епсг?Р01пг ( зТаТЬс чоаб Маап() ( ЕчепТЬоддег.игЬТеЬод( лаэрегистрирозать это!" ) ) В результате запуска этого кода будет получен следующий вывод: бпиапб1еб ЕхсеРТЬоп: яузгею.туре1пЬТ1а11гасаопЕхсерТ1оп: тие Туре ЬпЬТ1а11гег Тот 'Ечепгьоддег' ТЬгеч ап ехсерсаоп.
†> Бузгет.ии11аесегепсеххсеРТ1оп: ОЬ)есс гегегепсе пог зег То ап Ьпзгапсе ог эп оЬ)есг. аг ЕчепТЬоддег..ссТог() — Епб ос 1пгег ехсерТ1оп зсасн Тгасе-- аТ ЕпТгуРоапТ.Ма1п() Необработанное исключение: Буягею.туретпгтаа11гасаопххсерс1оп: Инициалиэатор типа Ечепгсоддег сгенерировал исключение. †> Яуягею.ни11негегепсеЕхсергаопт Объектная ссылка не установлена з экземпляр объекта. в Ечепгсоддег..ссгог О -- Конец трассировки стека внутреннего исключения в ЕпсгуРозпс.наап 0 Обратите внимание, что наряду с указанием того, что внешнее исключение имеет тип Туре1пЬТЬа11гаТЬопЕхсерсаоп, вывод такжепоказывает внутреннееисключение, с которого все началось — Мп11кегегепсеЕхсерТЬоп.
Кто должен обрабатывать исключения? (де должны обрабатываться исключения? Ответ можно найти, применив вариант шаблона Ехрегт (Эксперт), который устанавливает, что работа должна выполняться сущностью. являющейся экспертом в данной области. Это — окольный путь уведомить, что исключение должно перехватываться в точке, где его действительно можно обработать с уровнем знаний, достаточным для того, чтобы справиться с исключительной ситуацией наилучшим образом. Иногда перехватывающая сутцность может находиться близко к точке генерации исключения внутри фрейма стека. Код может перехватить исключение, затем предпринять некоторые действия по исправлению ситуации, после чего позволить программе нормально продолжить выполнение.
В других случаях единственным разумным местом для перехвата исключения будет метод Маап точки входа, где процесс можно либо прервать после выдачи некоторых полезных данных. либо сбросить, как если бы приложение было только что перезапущено.
Втавный вывод состоит в том, что по возможности должен быть найден лучший способ восстановления после исключения, и лучшее место определяется тем, где это наиболее целесообразно сделать. Не забывайте, что во многих случаях восстановление после исключительной ситуации внутри контекста работающего приложения невозможно. 200 Глава 7 Избегайте применения исключений для управления потоком выполнения Может возникнуть искушение применять исключения для управления потоком выполнения в сложных методах.
Это никогда нельзя считать хорошей идеей по двум причинам. Во-первых, генерация и обработка исключений обходится дорого. Поэтому. если вы хотите использовать их для управления потоком выполнения внутри метода, находящегося в ядре приложения, его производительность, скорее всего, снизится. Во-вторых, это нивелирует саму природу исключений. Йтавное назначение исключений — указать на исключительные условия таким образом, чтобы их можно было четко обработать или сообщить о них. Исторически сложилось так, что программисты достаточно ленивы в отношении обработки исключительных условий. Сколько раз приходилось видеть код, где программист даже не позаботился проверить значение, возвращенное АР!-функцией или вызовом метода? Такие пассивные подходы к обработке ошибок могут быстро привести к серьезным проблемам.
Исключения представляют собой синтаксически сжатый и унифицированный способ для описания и обработки ошибочных условий, который позволяет не засорять код множеством блоков 1 Г и других традиционных гне связанных с исключениями) конструкций обработки ошибок. В то же время исполняющая система поддерживает исключения и выполняет огромную работу, когда исключение сгенерировано. Раскрутка стека — сама по себе нетривиальная задача. В конечном итоге точка, в которой сгенерировано исключение, и точка, где оно обработано, могут находиться далеко друг от друга и не иметь прямой связи. Поэтому бывает трудно при чтении кода определить, где исключение будет перехвачено и обработано.
Только этих причин достаточно для того, чтобы придерживаться традиционных приемов при управлении нормальным потоком выполнения. На заметку! На зту тему вы можете найти статью Тое Созт от бксерзопз (Цена исключений) в блоге Рико Марианн (Я)со Маг)ап)) по адресу пгср: /гЬ1ооз.глзг)п. сов!касок/ агс)т1чег2003г12г19/44097.азрх.
Рико является экспертом в области проблемы производительности СЫ. Обеспечение нейтральности к исключениям Когда исключения были впервые добавлены к С+ч-, многие разработчики были впечатлены возможностью генерировать их, перехватывать и обрабатывать. Фактически в то время существовало распространенное заблуждение, что обработка исключений состоит из стратегического размещения операторов с ту по всему коду и добавления гЬгон при необходимости.












