Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 35
Текст из файла (страница 35)
В СЯ исключение, сгенерированное в финализаторе и покидающее его не перехваченным, будет трактоваться как необработанное исключение. и по умолчанию процесс будет прерван после уведомления об исключении. На заметку! В .НЕТ 20 это поведение изменилось по сравнению с тем, что было в.НЕТ 1.1. До появления .НЕТ 2,0 необработанные исключения в потоке финализации просто "проглатывались" после уведомления пользователя, а затем процессу было позволено продолжаться. Опасность такого поведения заключается в том, что система могла после этого работать в полуисправном и несогласованном состоянии.
Поэтому лучше уничтожить процесс, чем продолжать его работу с риском причинения большего вреда. В главе 7 будет показано, как заставить СЫ вернуться к старому поведению, если оно абсолютно необходимо. Одноразовые объекты Ранее в разделе, посвященном финализаторам, говорилось об отличиях между детерминированным и недетерминированным уничтожением, а также было показано, что детерминированное уничтожение сопряжено со значительными неудобствами. В связи с этим был предложен интерфейс 101эрозэЪ1е, который на самом деле был добавлен еще на этапе бета-тестирования первого выпуска .НЕТ РгатпеиогК когда разработчики страдали от отсутствия какой-либо встроенной формы детерминированной финализации.
Это не идеальная замена детерминированной финализации, но она решает свою задачу ценой дополнительной сложности для клиентов объектов. Классы, структуры и объекты 129 Интерфейс ПИ.врозаЪ1е Определение интерфейса 101зрозаЬ1е показано ниже: риЬ)(с 1пгегсасе 101эроззЬ1е ( чок 1 Осэроэе (); Обратите внимание, что здесь имеется только один метод 01зрозе, в реализации которого должна выполняться вся черновая работа.
То есть внутри этого метода объект должен быть полностью очищен, а все ресурсы освобождены. Несмотря на то что Оазрозе вызывается клиентским кодом. а не автоматически системой, все же это хороший способ для клиентского кода заявить: "работа с этим объектом завершена и он не будет использоваться вновь". Хотя шаблон Р(зрозаЫе и представляет собой форму детерминированного уничтожения, все же это решение далеко от идеала. При использовании 101зрозаЬ1е ответственность за вызов метода 01зрозе возлагается на клиента. У клиента нет никакой возможности поручить системе или компилятору его автоматический вызов.
С)) немного облегчает эту задачу в случае исключений. перегружая ключевое слово эз1по, о чем речь пойдет в следующем разделе, При реализации метода оазрозе класс обычно строится таким образом, что код финализатора повторно использует Ор ерове. Таким образом, если клиентский код никогда не вызовет оазрозе, то код финализатора позаботится об этом в свое время. Другой фактор делает реализацию 101зрозаЬ1е болезненной для объектов, и он связан с необходимостью объединения в цепочку вызовов 101зрозаЬ1е, если объекты содержат ссылки на другие объекты, поддерживающие 101зрозаЬ1е.
Это несколько затрудняет проектирование классов, поскольку должно быть известно, реализует ли класс, используемый в качестве типа поля, интерфейс 101зроэаЬ1е, и если да, то также понадобится реализовать 101зрозаЬ1е и не забыть вызвать его метод 01зрозе. С учетом всего сказанного об интерфейсе 101зрозаЬ1е становится ясным, насколько сборщик мусора добавляет сложности в дизайн, хотя и сокращает вероятность утечек памяти. Это не значит, что сборщик мусора хуже; на самом деле.
при правильном применении он очень полезен. Однако, как и в любом дизайне, все инженерные решения обычно имеют свои "плюсы" и "минусы". Рассмотрим пример реализации 101эрозаЬ1е: пэ1пц Зуэгелп рпЬ11с с1аэз А: 101зрозаЬ1е ( Рг(чэге Ьоо1 б1зрозеб = Га1зе) ргачаге чо16 01эроэе( Ьпо1 б1зрозапд ) ( 11( !базрозеб ) ( 11( бсзрпзапч ) ,тт' Здесь безопасно обрекаться к другим объектам Оопзо1е.ыхагеьапе( "Очистка объекта" )т бсзрозеб = сгпе) ) ) рпЬ11с чоаб Осзрозе() Оаэроэе( Сгпе )т ОО.Зпрргеззукпз11зе( Сказ )) 130 Глава 4 рпЬ11с яо1с) Оозотесьгпя() ( Сопзо1е.иггсеьзпе ( "й. Яозопегьупд ()" ); ) -)) О ( Сопзо1е.кгтсеъгпе( "Финализапия" Оьзрозе( Га1зе ); ) ) рпЬаус с1азз Япсгуго1пп ( зсастс яо1О Магп() ( и а = пеэ зО; ггу ( а.созопеГП1пд()! 11па11у ( а.Разрезе(): ) ) ) Разберем детально этот код, чтобы понять, что в нем происходит в действительности.
Первое, что следует отметить в классе — внутреннее булевское поле, регистрирующее факт вызова метода Рузрозе для данного объекта. Оно присутствует потому, что вполне допускается, чтобы нлиентсний код вызывал о1зрозе несколько раз. Поэтому нужен какой-то способ узнать, что работа уже выполнена. Вдобавок построен финализатор в терминах реализации Р1зрозе. Обратите внимание, что предусмотрено две перегрузки О1зрозе. Зто сделано для того, чтобы внутри метода Рузрозе (Ьоо1) было известно, вызван он через 1отзрозаЬ1е. Оазрозе или через деструктор.
Это позволяет понять, можно ли безопасно обращаться в методе к содержащимся объектам. Еще один момент: метод Рузрозе осуществляет вызов РС. Яцрргеззу1па11зе. Этот метод сборщика мусора позволяет предотвратить финализацию объекта сборщиком мусора. Если клиентский код вызывает Разрозе, и этот метод полностью очищает ресурсы, включая всю работу по финализации, то в дополнительной финзлизацин данного объекта нет необходимости. Для предотвращения финализации объекта можно вызвать япрргеззу).па11зе. Такая полезная оптимизация помогает сборщику мусора своевременно избавиться от объекта, когда все ссылки на него прекращают свое существование.
Теперь давайте посмотрим, как использовать этот одноразовый объект. Обратите внимание на блок сгу/г1па11у внутри метода иаап. Исключения подробно рассматриваются в главе 7, а пока достаточно знать, что конструкция Сгу/г1па11у гарантирует выполнение определенного кода вне зависимости от того, как завершится выполнение блока кода. В данном случае, независимо от того, как поток управления покинет блок ггу — нормально через оператор гесигп или даже по исключению — код в блоке г1па11у будет выполнен. Рассматривайте блок 11па11у как страховочную сетку. Речь идет о блоке Я1па11у, в котором вызывается Рбзрозе объекта.
Независимо от обстоятельств Обзрозе будет вызван обязательно. Зто блестящий пример того, нак недетерминированная финализация возлагает ответственность на клиентский код, или на пользователя, за очистку объекта, в то время Классы, структуры и объекты 131 как детерминированная финализация не требует от пользователя связываться с написанием не особо изящных блоков ггу/11па11у или вызовом 01зрозе. Это определенно усложняет жизнь пользователю, поскольку ему нужно писать устойчивый и/или нейтральный к исключениям код.
Проектировщики Са попытались облегчить это бремя, перегрузив ключевое слово пз).по. Но хотя это и снижает нагрузку, все же не позволяет полностью освободить пользователя от дополнительного кодирования. На заметку! С++/СН позволяет испольэовать КАП способом, знакомым разработчикам С++, не требуя явного вызова 01зрозе или применения блока пз1по, Было бы замечательно, если бы язык СЗ позволял то же самое, но, к сожалению, такое кардинальное изменение з языке повлекло бы за собой много неприятностей.
Ключевое слово ив~п9 Ключевое слово пег по было перегружено для поддержки шаблона О(зрозаЫе. Общая иден состояла в том, что оператор пз1по должен захватывать ресурсы внутри фигурных скобок, следующих за ключевым словом пз1по, в то время как область видимости этих локальных переменных ограничена областью определения следующих далее фигурных скобок. Рассмотриммодифицированную версию предыдущего примера; пз1по Зузгеки рпЬ11с с1ззз А: 101зроззЬ1е ( рггчаге Ьоо1 б1зрозеб = Гз1зек ргтчзсе чогб рвзрозе( Ьоо1 бгзрозгпо ) ( 11( !оузрозеб ) ( 11 ( б1зрозгпч ) ( // Здесь безопасно обрекаться н другим объектам ) Сопзо1е.нг1се01пе( "Очистка объекта" )к бгзрозеб = Ггпе; ) рпЫгс чотб рвзрозе() ( 01зрозе( Ггпе ); ОС.Зпрргеззутпа11ге( гпзз ); рпЫтс чогб бозовеспгпд() ( Сопзо1е.нгггеьзпе( "А.ЗозотеГЫпо()" ); ) А() ( Сопзо1е.кгзсеьгпе( уаиналиээпия" ); рвзрозе( Гэ1зе )) ) ) рпЫгс с1азз ЕпсгуРозпс ( згагзс чо1б Мега() ( 132 Глава 4 изт.пд( А а = пеи А() ) ( а.позо1кеЕЬьпд (); ) иввпд( А а = пеи А() Ъ = пеи А() ) ( а.рововвЕЬьпд(); Ь.РововеЕЬ№пд(); ) ) Все изменения касаются метода Маэп.
Обратите внимание, что неуклюжая конструкция ггуу Ггпа11у заменена более ясным оператором из№пд. "За кулисами" оператор цзтпд расширяется до конструкции Ггу№ Гэпэ11у, которая была раньше. Но теперь этот код намного легче читать и понимать. Тем не менее, клиентский код по-прежнему не избавлен от необходимости помнить об использовании оператора цзфпд. Оператор цз№пд требует, чтобы все ресурсы.
захваченные в процессе, были неявно преобразуемыми к 1Р1зроззЬ1е. То есть они должны реализовывать 1Р1зроззЬ1е. В противном случае, во время компиляции будут выдаваться предупреждения. Типы параметров методов Параметры методов подчиняются таким же общим правилам, которые приняты в языках С и С++. То есть по умолчанию параметры объявляют идентификатор переменной, который действителен на протяжении и в контексте самого метода. Здесь нет константных параметров, как в С++. и параметрам методов можно присваивать значения.
Если только параметры не объявлены определенным образом — как гег или оцс — такое присваивание остается локальным в пределах метода. Обнаружилось, что одной иэ наибольших заминок для разработчиков С++ в языке С№ стало обращение с семантикой переменных, передаваемых в методы. Поскольку доминантным типом внутри СЕВ является ссылка, переменные таких объектов просто указывают на экземпляры в куче, т.е. аргументы передаются в метод в соответствие с семантикой ссылки. Разработчики С++ привыкли к тому, что по умолчанию в методы передаются копии переменных, если только они не переданы по ссылке или в виде указателя.
Другими словами, в С++ аргументы передаются согласно семантике значений. В С№ аргументы в действительности тоже передаются по значению. Однако для ссылок копируемое значение представляет собой саму ссылку, а не объект. на который она ссылается. Изменения в состоянии осыпаемого объекта внутри метода становятся видимыми коду, вызвавшему этот метод.












