Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 54
Текст из файла (страница 54)
почему он потерпел неудачу. В предыдущем коде это сделано не было. Метод не может просто спокойно вернуть управление, как будто все прошло гладко, когда на самом деле действие завершить не удалось. В-третьих, код получился просто громоздким и трудно читаемым. И последнее: в этом коде присутствуют и другие, менее существенные проблемы, которые для простоты рассматриваться не будут. Так каково решение? Подумайте о том, что вы пытаетесь сделать с помощью операторов сгу. Вы хотите выполнить действия, которые, возможно, сгенерируют исключение, и если это произоидет, то вернуться к предыдущему состоянию.
На самом деле можно разработать вариант кода без операторов сгу, который будет работать следующим образом: сначала попытаться выполнить в одном методе все действия, которые могут сгенерировать исключение, и за последним иэ них зафиксировать изменения с помощью операций, не генерирующих исключения. На заметку! Сообщество программистов на С+ь признало такую технику, отчасти, благодаря блестящей работе, опубликованной Хербом Саттером (НегЬ Зойег) в его серии Бхсербопа! С++ (Аоб)зоп-(З/ез(еу Рго(евзюпа!).
Ничто не мешает применять эту технику и в мире С№. 204 Глава Т пябпд Яуясеш. Со11ессбопя) с1аяя Ешр1оуее ( ) с1аяя Ешр1оуеебапаЬаяе роЫЬс чогб ТегшбпасеЕпр1оуее( Ьпс Ьпбех ) ( /с' Клонирояать важнейшие объекты. Аггауьгяс ГешрАссгчеЕшр1оуеея = (АггауЬ1яп) асс1чеЕшр1оуеея.С1опе(); АггауЬгяс ГешрТегш1пасебЕшр1оуеея = (АггауЫяс) Гегш1пасебЕшр1оуеея.С1опе()) сс/ Выполнить действия над временными объектами. оЬЯесп ешр1оуее = гешрлссгчеашр1оуеея [гпбех) > ГешрлссгчеЕшр1оуеея.кешочеАГ( гпбех ); ГешрТегш1папебЕшр1оуеея.Абб( ешр1оуее ); ,сс' Зафиксировать изменения. АггауЬ1яо Гешрярасе = по11; Ьуясэнар( гев ассучеЕшр1оуеея, гег ГешрАсо1чеЕшр1оуеея, гег ГепрЯрасе ); Ьгясянар( гег ГегпбпасебЕшр1оуеея, гег ГешрТегшгпасебЕшр1оуеея, геГ гешрярасе ): чо1б Ьгясэнар( гег Аггауьфяс Тггяс, гег Аггауьуяс яесопс(, гег АггауЬ1яс Гешр ) ( лепр = Т1гяс) ТЬгяс = яесопбл яесопб = Гешр; Гепр = по11; ) рггчасе Аггауьфяс аспгчеЕшр1оуееял рггчасе АггауЬгяс ГегшгпасебЕшр1оуеея; Для начала обратите внимание на отсутствие операторов г ту.
В их отсутствии замечательно то, что методу не нужен код возврата. Вызывающий код может ожидать, что метод либо работает, как обещано, либо генерирует исключение. Состояние системы затрагивается только двумя строками в методе — последние два вызова ьуягянар. Метод ьфягянар позволяет заменить ссылки на объекты АггауьЬяг в ешр1оуееВагаьаяе ссылками на временные модифицированные копии. Как такая техника может быть лучше, если она выглядит намного менее эффективнойу Секретов два. Один, очевидный, состоит в том, что независимо от того, где бы в этом методе не генерировалось исключение, состояние Ешр1оуеебасаЬаяе останется незатронутым.
Но что, если исключение произойдет внутри Ьуягэнар7 Здесь кроется другой секрет: 1,1. згянар никогда не генерирует исключений. Одним нз наиболее важных условий, необходимых для создания нейтрального к исключениям кода, является наличие набора операций, которые гарантированно выполняются без сбоев при нормальных условиях. Случаи, когда какой-нибудь "умелец" выдернет вилку компьютера из розетки во время выполнения Ьу я Гя нар, либо в этот момент произойдет землетрясение или налетит торнадо, не рассматриваются. Давайте посмотрим, почему ЬфяГЯнар нс генерирует исключений. Обязательно прочитайте раздел главы 13, посвященный интерфейсу 1С1опеаЫе, в котором показано, как избегать его реализации поначалу.
Безопасность и обработка исключений 205 Для того чтобы создать нейтральный в отношении исключений код. нужно иметь несколько операций, таких как операция присваивания, которые гарантированно не генерируют исключений. К счастью, СЬК предоставляет такие операции. Одним из примеров может служить присваиваиие ссылок, когда не требуется никаких преобразований типа. Кзядая ссылка указывает на объект, хранящийся в определенном месте, и это место имеет ассоциированный с ним тип. Однако, как только место выделено и существует, копирование ссылок с одного на другое представляет собой простое копирование уже выделенных мест, подобное копированию обычных указателей в Сээ, которое просто не может вызвать сбой.
Это отлично работает, когда нужно скопировать ссылку одного типа на ссылку того же самого типа. Но что случится, когда потребуется преобразование? Может ли оно привести к генерации исключенияу В стандарте СФ указано, что неявные операции преобразования никогда не генерируют исключений. Если присваивание подразумевает неявное преобразование, вы защищены, если исходить из того, что пользовательские операции неявного преобразования следуют стандарту и не генерируют исключений'. Если вы найдете пользовательскую операцию неявного преобразования, которая генерирует исключения, рекомендуется разработчику этой операции немедленно предъявить спецификацию Со.
Однако явные преобразования в форме приведений могут генерировать исключения. Подытожим: простое присваивание одной ссылки другой, независимо от того, требует оно неявного преобразования или нет, не приводит к генерации исключений. Простое присваивание одной ссылки на место в памяти другой — это все, что делает 11згзиар. После того, как временные объекты Аггауьазс приведены в нужное состояние и вызывается 11зкзиар, значит, достигнута точка, в которой можно иметь уверенность, что никаких исключений в методе Тегюапасектр1оуее уже не произойдет.
Теперь можно безопасно выполнить замену. Объекты Аггау11зг в Блтр1оуеепакзЬазе заменяются временными объектами. По завершении метода исходные объекты Аггауьазс готовы к тому, чтобы их удалил сборщик мусора. Относительно Бьзсанар следует отметить еще один момент: временное место для хранения экземпляра Аггау11зг во время обмена выделяется вне метода 11зСБиар и передается ему как параметр ге 1. Это делается для того, чтобы избежать исключения Бгаскоттег11оидхсер11оп внутри А1зСБнар. Существует минимальная вероятность, что при вызове БазкБэар стек будет заполнен, и простое выделение очередного пространства в стеке завершится неудачей и вызовет исключение.
Поэтому данный шаг выполняется за пределами метода 11зСБиар. При входе в 1 Тзгаэар вся необходимая память выделена и готова к использованию. Эта техника, применяемая слишком часто в системе, требующей высокой стабильности, быстро приводит к появлению чересчур сложных методов, которые нужно разбивать на более мелкие функциональные единицы. По сути, зта идиома повышает сложность метода, к которому она применена. Поэтому, если обнаруживается, что обеспечение "пуленепробиваемости"метода становится трудным и громоздким, стоит проанализировать его и посмотреть, не предпринимается ли попытка сразу выполнить слишком много работы, которую можно было бы разбить на более мелкие порции.
Иногда возникает необходимость в том, чтобы сделать операции обмена, подобные ЬазсБиар, атомарными для многопоточной среды. Код ЬазСБиар можно модифицировать для использования некоторого рода блокирующего объекта, такого как семафор или объект Бузсетл. тпгеа61по. мос 11ог. Но при этом есть риск непреднамеренно сделать 1.1з с авар способным генерировать исключения, что нарушит предъявляемые к нему требования. К счастью, пространство имен Бузгею. Тттгеаб1пд предлагает класс Таге г1оскеб для вы- В руководстве по СО четко указано, что пользовательские операции преобразования не должны генерировать исключений. 206 Глава 7 полнения таких операций обмена в атомарном режиме, причем методы гарантированно не генерируют исключения.
Класс 1птет1осхеб предоставляет обобщенные перегрузки для всех полезных методов, обеспечивая нх высокую эффективность. Обобщенные методы 1п те т1ос хеб ограничены работой только со ссылочными типами. Дополнительные сведения об использовании класса 1отет1осхеб можно найти в главе 12. В качестве резюме к сказанному; все, что может сгенерировать исключение, должно делаться перед модификацией состояния объекта, над которым проиаводится операция.
Пройдя последнюю точку, где возможны какие-либо исключения. зафиксируйте изменения с использованием операций, гарантированно не генерирующих исключения. Если задача состоит в создании устойчивой реальной системы, на целостность которой полагается множество людей, трудно переоценить ценность этой идиомьс. Конечно, она не так эффективна во время выполнения, как наивный подход, и требует больше системных ресурсов для эффективной работы, но клиенты всегда отдадуг предпочтение неэффективности перед повреждением данных. Коллеги также поблагодарят вас, поскольку утечки ресурсов и прочие неприятности, являющиеся побочным эффектом генерации исключений. очень трудно находить из-за их "бессвязной" природы.
Ограниченные области выполнения Пример из предыдущего раздела демонстрирует некоторый уровень паранойи, который следует допускать для написания "пуленепробиваемого" нейтрального к исключениям кода. Чтобы предотвратить исюпочение, связанное с переполнением стека, перед вызовом 11зтеаар даже было выделено необходимое дополнительное пространство. Может показаться, что все неприятности учтены.
К сожалению, это не так. В среде СЬК могут случаться другие асинхронные исключения. такие как Тйтеас1АЬоттЕхсертвоп (о котором речь пойдет в главе 12), СотОГИевотуЕхсертьоп и ЕтасЮоет51онЕхсерт1оп. Например, что если на фазе фиксации изменений метода тетлс1пзтеЕлср1оуее домен приложения будет остановлен, вызвав исключение ТЬтеабАЬоттехсерт1опу Или что если во время первого вызова 11зтзкар компилятор ЛТ не сможет выделить достаточно памяти для первоначальной компиляции методами Ясно, что с такой неприятной ситуацией справиться нелегко. Фактически во времена .НЕТ 1.1 в таких случаях мало, что можно было сделать.
Однако, начиная с .1ЧЕТ 2.0, можно воспользоваться ограниченной обласгпью выполнения (Сопз1га1пес1 Ехесипоп Кея1оп — СЕЯ) или кршпичным сбцнюлазалю)юм. СЕВ представляет собой участок кода, который среда СЬК подготавливает до выполнения, так что когда в нем возникает потребность, все необходимое имеется под рукой, и вероятность сбоя минимальна.











