Избегайте утилизации определенных системными экземплярами Pen и Brush - PullRequest
11 голосов
/ 08 июля 2010

Я понимаю, что лучше всего вызывать Dispose () для экземпляров Pen и Brush , за исключением случаев, когда для них заданы системные предопределенные значения (например, *). 1005 * System.Drawing.Brushes , System.Drawing.Pens или System.Drawing.SystemBrushes )

Попытка удалить системный ресурс приводит к возникновению исключения.

Кажется неочевидным, как вы можете определить (кроме упаковки вызова Dispose () в try / catch), ссылается ли один из этих ресурсов на системное или пользовательское значение.

Ответы [ 8 ]

30 голосов
/ 17 июля 2013

Это старый вопрос, который я знаю, но я проводил некоторые исследования утечек ресурсов, связанных с объектами GDI, и почти каждое утверждение в принятом ответе ложно.В интересах точности для более поздних читателей, которые находят этот вопрос с помощью поиска, как я сделал:

Нет необходимости вызывать Dispose.

Это утверждение вводит в заблуждение.Хотя технически вы можете обойтись без звонка Dispose, крайне плохая практика - не выбрасывать кисть или ручку , которыми вы владеете , когда закончите с ней.

Причина в том, что существует жесткое ограничение (установленное по умолчанию на десять тысяч, хотя оно может быть увеличено с помощью взломов реестра) количества объектов GDI, которые могут быть выдающимися в процессе - не в домене приложения, а в примечании - исборщик мусора может не успеть доработать ресурсы быстрее, чем они просочились.Распределение управляемой памяти создает давление сбора , но соответствующее давление завершения отсутствует.

Целью сбора мусора является устранение подобных требований.

Это не так.Целью сбора мусора является эмуляция среды с произвольно большим объемом памяти.

Одна из основных целей IDisposable - позволить классу очищать неуправляемые ресурсы в средах с ограниченными ресурсами.

Это правильно.

Если не вызывать метод Dispose(), неуправляемые ресурсы класса будут очищены после того, как объект будет завершен и удаленво время сбора мусора.

Это иногда правильно;это верно для объектов GDI.Это хорошая практика для классов, которые держатся за неуправляемые ресурсы, для реализации семантики финализации в качестве поддержки;дополнительный уровень защиты для людей, которые следуют плохим советам, данным в принятом ответе. Вы не должны полагаться на финализаторы, чтобы спасти вас от ваших ошибок;Вы должны распоряжаться своими ресурсами.

Вы должны полагаться только на финализатор, чтобы справляться с безумными исключительными ситуациями.Рассмотрим для примера:

Brush myBrush = MakeMeABrush();
DoSomethingWithBrush(myBrush);
myBrush.Dispose();

Предположим, что исключение прерывания потока генерируется после выделения кисти, но до назначения myBrush.Никакая комбинация try-catch-finally не позволит вам очистить эту кисть с помощью Dispose;вам придется положиться на финализатор.Вот для чего нужен финализатор: совершенно сумасшедшие ситуации, когда вы не можете распоряжаться собой. Это не повод быть небрежным.

Если вы "должны" вызвать dispose, и вы не знаете, является ли экземпляр кисти "системным" или "нормальным"- "кисть, тогда вам придется использовать блок try ... catch.

Хотя это опять-таки технически правильно, оно полностью упускает смысл. Если вы находитесь в ситуации, когда вы не знаете, владеете ли вы кистью или нет, у вас есть ошибка в дизайне вашей программы .Не печатайте на своих ошибках с try-catch блоком!Исправьте ошибку!

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

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

Если договор, по которому вы принимаете решение, заключается в том, что как производитель, так и потребитель собираются освободить ресурс, потребитель должен вызвать Clone на каждую кисть, переданную, чтобы убедиться, что у него есть одноразовый сейф.ресурс, и что производитель продолжает владеть действительным ресурсом.

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

Но контракт «никто не убирает его, и мы надеемся на лучшее» - довольно плохой контракт.

Не нужно звонить Dispose() на SystemBrushesи SystemPens, поскольку библиотека GDI + позаботится об этих ресурсах.

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

Раздел «Замечания» классаотметит, если есть требование для вызова метода Dispose.

Это утверждение неверно.Вы должны исходить из предположения, что ресурс Dispose IDisposable всегда необходим, если у вас нет веских оснований полагать иначе.Отсутствие строки в документации не является доказательством того, что утилизация не нужна.

В заключение положительная нота:

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

Это хорошая практика.Если количество созданных кистей и ручек относительно невелико и одни и те же используются снова и снова, то имеет смысл постоянно их кэшировать.Поскольку их время жизни подходит к концу программы, нет необходимости распоряжаться ими.В этом случае мы не выбрасываем мусор, потому что это не мусор ;это полезноОбъекты GDI, которые не кэшированы, должны, конечно, все еще располагаться.Опять же, следование стратегии кэширования не является оправданием для использования плохих практик.

4 голосов
/ 06 декабря 2012

Когда вы закончите работу с созданным графическим объектом, вы должны избавиться от него, вызвав его метод Dispose.(Это правило верно для многих различных объектов GDI +.) Не храните его в течение дождливого дня, потому что оно не будет действовать позже.Вы должны, должны, должны избавиться от него, когда закончите с этим.В противном случае это может привести к повреждению изображения, проблемам с использованием памяти или, что еще хуже, международному вооруженному конфликту.Поэтому, пожалуйста, утилизируйте все графические объекты должным образом.

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

Получено отсюда http://msdn.microsoft.com/en-us/library/orm-9780596518431-01-18.aspx

2 голосов
/ 30 сентября 2011

Краткий ответ: если вы его создадите, либо делегируйте ответственность за очистку, либо сами убирайте объекты. Вы можете создавать «утечки» ресурсов GDI, позволяя вещам зависать в сборщике мусора. В конце концов они могут быть убраны, но они не приносят никакой пользы. то есть, если вы не вызываете «закрыть» или «Утилизировать открытые файлы», файл остается заблокированным до тех пор, пока ГХ не «обходит его» * ​​1001 *

2 голосов
/ 22 июля 2010

Прежде всего, вы всегда должны утилизировать щетки, когда можете, а не оставлять их до сборщика мусора. В то время как GDI в конечном итоге приступит к заботе о таких вещах (при условии, что библиотека будет закрыта должным образом), неизвестно, когда это может произойти. На самом деле, я понимаю, что ручки кисти остаются на долгое время. В течение длительного времени приложения вы наблюдаете утечку памяти de facto . Конечно, в небольшом приложении, которое не будет работать долго, или в приложении, которое редко создает кисти, вы можете позволить системе справиться с этим, но мне это кажется небрежным.

Как правило, всякий раз, когда я создаю кисть, я делаю это в операторе using. Это автоматически вызывает удаление кисти, не беспокоясь об этом. В качестве дополнительного бонуса, поскольку вы создаете кисть внутри оператора, вы знаете, что это не предопределенная кисть. Каждый раз, когда вы создаете и используете ненастроенную кисть, оберните блок в использование, и вам не нужно об этом беспокоиться вообще. На самом деле, вам даже не нужно явно вызывать Dispose, поскольку блок будет делать это даже в случае исключения.

0 голосов
/ 22 июля 2010

Просто для полноты, используя Reflector, я вижу, что классы System.Drawing.Pen и System.Drawing.SolidBrush имеют закрытое поле с именем «immutable».

Это поле имеет значение true, когда объект ссылается на системный ресурс, поэтому вы можете использовать отражение, чтобы тщательно проверить значение этого поля и решить, следует ли вызывать Dispose () для него.

0 голосов
/ 09 июля 2010

Нет необходимости звонить Dispose. Целью сбора мусора является устранение подобных требований.

Одна из основных целей IDisposable - позволить классу очищать неуправляемые ресурсы в средах с ограниченными ресурсами. Если вы не вызовете метод dispose, неуправляемые ресурсы класса будут очищены после того, как объект будет завершен и удален во время сборки мусора.

Если вы «вынуждены» вызывать dispose и не знаете, является ли кисть «системной» или «нормальной», тогда вам придется использовать блок try ... catch.

  • Нет необходимости вызывать dispose для SystemBrushes и SystemPens, потому что библиотека GDI + позаботится об этих ресурсах.
  • Хорошо, чтобы избавиться от SystemFonts и SystemIcons.

В разделе Remarks класса будет указано, есть ли необходимость вызывать метод Dispose. Если в разделе «Примечания» рекомендуется вызвать метод Dispose, я сделаю это.

В общем, я не призываю утилизировать на ручках и кистях. Если у меня есть графическое приложение или класс, я буду кэшировать экземпляры нужных мне ручек и кистей. Я использую их на протяжении всей жизни приложения или класса. Если бы я не сделал этого, производительность графической графики пострадала бы от попыток создавать и удалять все эти объекты столько раз и так часто. (Хм ... теперь, когда я думаю об этом, производительность, вероятно, является причиной того, что мы не можем избавиться от SystemBrushes и SystemPens, но можем избавиться от SystemFonts и SystemIcons. Даже инфраструктура кэширует SystemBrushes и SystemPens.)

0 голосов
/ 08 июля 2010

Единственное, что приходит на ум, - это не практикуйте системные ручки / кисти в качестве параметров методов.

0 голосов
/ 08 июля 2010

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

...