C # позволяет мне делать следующее (пример из MSDN):
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
Что будет, если font4 = new Font
бросит? Насколько я понимаю, у font3 будут утечки ресурсов, и он не будет утилизирован.
- Это правда? (font4 не будет утилизирован)
- Означает ли это, что
using(... , ...)
следует вообще избегать использования вложенного кода?
c#
using
using-statement
Бенджамин Грюнбаум
источник
источник
using(... , ...)
будет скомпилирован во вложенные блоки, но я этого точно не знаю.using
вообще, GC все равно его заберет.finally
блок, он не попал бы в блок, пока не были созданы все ресурсы.using
вtry
-finally
выражение инициализации оценивается внеtry
. Так что это разумный вопрос.Ответы:
Нет.
Компилятор сгенерирует отдельный
finally
блок для каждой переменной.В спецификации (§8.13) говорится:
источник
ОБНОВЛЕНИЕ : я использовал этот вопрос как основу для статьи, которую можно найти здесь ; см. это для дополнительного обсуждения этого вопроса. Спасибо за хороший вопрос!
Хотя ответ Шабсе, конечно, правильный и отвечает на заданный вопрос, есть важный вариант вашего вопроса, который вы не задавали:
Позвольте мне прояснить это немного подробнее. Предположим, у нас есть:
Теперь у нас есть
Это то же самое, что и
ХОРОШО. Допустим
Whatever
кидает. Затемfinally
блок запускается, и ресурс освобождается. Нет проблем.Допустим
Blah1()
кидает. Затем бросок происходит до выделения ресурса. Объект был выделен, но ctor никогда не возвращается, поэтомуfoo
никогда не заполняется. Мы никогдаtry
не вводили объект, поэтому мы никогда не вводим егоfinally
. Ссылка на объект потеряна. В конце концов сборщик мусора обнаружит это и поместит в очередь финализатора.handle
все еще равно нулю, поэтому финализатор ничего не делает. Обратите внимание на то, что финализатор должен быть устойчивым перед лицом финализируемого объекта, конструктор которого никогда не завершился . Вы необходимы написать настолько сильные финализаторы. Это еще одна причина, по которой вам следует доверить написание финализаторов экспертам, а не пытаться делать это самостоятельно.Допустим
Blah3()
кидает. Бросок происходит после выделения ресурса. Но опять же,foo
никогда не заполняется, мы никогда не входим вfinally
, и объект очищается потоком финализатора. На этот раз дескриптор не равен нулю, и финализатор очищает его. Опять же, финализатор работает с объектом, конструктор которого никогда не преуспел, но финализатор все равно запускается. Очевидно, так и должно быть, потому что на этот раз надо было поработать.А теперь допустим
Blah2()
кидает. Бросок происходит после того, как ресурс выделен, но доhandle
заполнения! Опять же, финализатор запустится, но сейчасhandle
он все еще равен нулю, и мы пропускаем дескриптор!Вам нужно написать чрезвычайно умный код, чтобы предотвратить эту утечку. Теперь, в случае с вашим
Font
ресурсом, кого это волнует? У нас утечка дескриптора шрифта, большое дело. Но если вы абсолютно решительно требуете, чтобы каждый неуправляемый ресурс был очищен, независимо от времени возникновения исключений, то перед вами стоит очень сложная проблема.CLR должна решить эту проблему с помощью блокировок. Начиная с C # 4, блокировки, использующие этот
lock
оператор, были реализованы следующим образом:Enter
был очень тщательно написан, так что независимо от того, какие исключения выбрасываются ,lockEntered
устанавливается значение true тогда и только тогда, когда блокировка действительно была взята. Если у вас похожие требования, то вам нужно написать:и напишите так
AllocateResource
умно,Monitor.Enter
чтобы, что бы ни происходило внутриAllocateResource
,handle
заполнялось тогда и только тогда, когда его нужно освободить.Описание методов для этого выходит за рамки этого ответа. Проконсультируйтесь со специалистом, если у вас есть это требование.
источник
Blah
вызовы методов. Что мешает возникновению ThreadAbortException в любой из этих точек?AllocateResource
но до присвоенияx
. В этот моментThreadAbortException
может произойти А. Кажется, что все здесь упускают мою точку зрения, а именно: создание ресурса и присвоение ссылки на него переменной не является атомарной операцией . Чтобы решить указанную мной проблему, вы должны сделать ее атомарной операцией.В дополнение к ответу @SLaks вот IL для вашего кода:
Обратите внимание на вложенные блоки try / finally.
источник
Этот код (на основе исходного образца):
Он создает следующий CIL (в Visual Studio 2013 , ориентированный на .NET 4.5.1):
Как видите,
try {}
блок запускается только после первого выделения, которое происходит вIL_0012
. На первый взгляд кажется, что это выделяет первый элемент в незащищенном коде. Однако обратите внимание, что результат сохраняется в ячейке 0. Если второе выделение не удается, выполняется внешнийfinally {}
блок, который выбирает объект из местоположения 0, то есть первое выделениеfont3
, и вызывает егоDispose()
метод.Интересно, что декомпиляция этой сборки с помощью dotPeek дает следующий восстановленный исходный код:
Декомпилированный код подтверждает, что все правильно и что по
using
существу расширен во вложенныеusing
s. Код CIL немного сбивает с толку, и мне пришлось смотреть на него в течение нескольких минут, прежде чем я правильно понял, что происходит, поэтому я не удивлен, что некоторые `` сказки старых жен '' начали прорастать вокруг этот. Однако сгенерированный код - неопровержимая правда.источник
Вот пример кода, подтверждающий ответ @SLaks:
источник
font4 = new Font
выкинет? Насколько я понимаю, в font3 будет утечка ресурсов, и он не будет удален».