Следует ли использовать Dispose только для типов, содержащих неуправляемые ресурсы?

Недавно я обсуждал с коллегой значение Dispose и типы, реализующие IDisposable.

Я думаю, что есть смысл в реализации IDisposable для типов, которые следует очищать как можно скорее, даже если нет неуправляемых ресурсов для очистки.

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

Мой аргумент заключался в том, что если у вас есть соединение ADO.NET, которое вы хотите закрыть как можно скорее, тогда реализация IDisposable и using new MyThingWithAConnection() будет иметь смысл. Мой коллега ответил, что под прикрытием соединение ADO.NET является неуправляемым ресурсом. Я ответил на его ответ, что в конечном итоге все является неуправляемым ресурсом.

Мне известен рекомендуемый шаблон одноразового использования, в котором вы <сильные > освобождают управляемые и неуправляемые ресурсы, если вызывается Dispose, но освобождают только неуправляемые ресурсы, если они вызываются через финализатор / деструктор (и некоторое время назад писал в блоге о том, как предупреждать потребителей о неправильном использовании ваших IDisposable типов)

Итак, у меня вопрос: если у вас есть тип, который не содержит неуправляемых ресурсов, стоит ли внедрять IDisposable?


person Steve Dunn    schedule 25.04.2012    source источник
comment
Как вы правильно заметили, соединение ADO является неуправляемым ресурсом.   -  person Konrad Rudolph    schedule 25.04.2012
comment
@KonradRudolph - Нет. Соединение называется управляемым ресурсом. Он содержит (владеет) неуправляемым ресурсом, хотя, вероятно, косвенно через SafeHandle.   -  person Henk Holterman    schedule 26.04.2012
comment
@Henk Вот что я имел в виду - я должен был сформулировать это более тщательно, но в вопросе это уже правильно сформулировано.   -  person Konrad Rudolph    schedule 26.04.2012
comment
Единственный раз, когда я когда-либо нуждался в IDisposable, помимо неуправляемых ресурсов, - это когда мне нужно убедиться, что события отписываются должным образом, чтобы класс можно было собрать с помощью сборщика мусора. Но это действительно провал языка: события ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО должны быть слабыми ссылками, но это не так.   -  person BlueRaja - Danny Pflughoeft    schedule 06.07.2012


Ответы (15)


Есть разные допустимые варианты использования IDisposable. Простой пример - открытый файл, который нужно закрыть в определенный момент, как только он вам больше не понадобится. Конечно, вы можете предоставить метод Close, но наличие его в Dispose и использование такого шаблона, как using (var f = new MyFile(path)) { /*process it*/ }, было бы более безопасным для исключений.

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

В общем, как только вы хотите добиться детерминированного уничтожения чего-либо, вам нужно реализовать IDisposable.

Разница между моим и вашим мнением заключается в том, что я реализую IDisposable, как только некоторый ресурс требует детерминированного уничтожения / освобождения, а не обязательно как можно скорее. В этом случае нельзя полагаться на сборку мусора (вопреки утверждениям вашего коллеги), потому что это происходит в непредсказуемый момент времени и на самом деле может не произойти вовсе!

Тот факт, что какой-либо ресурс неуправляемый под прикрытием, на самом деле ничего не значит: разработчик должен думать в терминах «когда и как правильно избавиться от этого объекта», а не «как он работает под прикрытием». Базовая реализация в любом случае может измениться со временем.

Фактически, одно из основных различий между C # и C ++ - отсутствие детерминированного уничтожения по умолчанию. IDisposable закрывает пробел: вы можете приказать детерминированное уничтожение (хотя вы не можете гарантировать, что клиенты его вызывают; точно так же в C ++ вы не можете быть уверены, что клиенты вызывают delete на объекте).


Небольшое дополнение: в чем на самом деле разница между детерминированным освобождением ресурсов и их освобождением как можно скорее? Собственно, это разные (хотя и не совсем ортогональные) понятия.

Если ресурсы должны быть освобождены детерминированно, это означает, что клиентский код должен иметь возможность сказать: «Теперь я хочу освободить этот ресурс». На самом деле это может быть не самый ранний возможный момент, когда ресурс может быть освобожден: объект, содержащий ресурс, мог получить все, что ему нужно, от ресурса, поэтому потенциально он может уже освободить ресурс. С другой стороны, объект может выбрать сохранение (обычно неуправляемого) ресурса даже после того, как объект Dispose прошел через него, очищая его только в финализаторе (если удерживание ресурса слишком долгое время не вызывает никаких проблем).

Итак, для освобождения ресурса как можно скорее, строго говоря, Dispose не требуется: объект может освободить ресурс, как только он осознает, что ресурс больше не нужен. Dispose, однако, служит полезным намеком на то, что объект сам больше не нужен, поэтому, возможно, ресурсы могут быть освобождены на этом этапе, если это необходимо.


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

Примеры: право на доступ к некоторой совместно используемой структуре (подумайте, RW-lock), огромный кусок памяти (представьте, что вы управляете частью памяти программы вручную), лицензию на использование какой-либо другой программы (представьте, что вам не разрешено запускать более X копии некоторой программы одновременно) и т. д. Здесь освобождаемый объект - это не неуправляемый ресурс, а право что-то делать / использовать, что является чисто внутренней конструкцией логики вашей программы.


Небольшое дополнение: вот небольшой список изящных примеров использования [ab] IDisposable: http://www.introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#IDisposable.

person Vlad    schedule 25.04.2012
comment
Когда вам когда-нибудь понадобится детерминированное разрушение, помимо неуправляемых ресурсов, таких как файлы и соединения с БД? (также небольшая проблема - сам объект не детерминированно разрушен, только ресурсы, которые он использует ... и только если эти ресурсы неуправляемые) - person BlueRaja - Danny Pflughoeft; 25.04.2012
comment
@ BlueRaja-DannyPflughoeft: См. Мой ответ, но отвечу прямо: 1) Когда вы используете эти ресурсы косвенно другими объектами, реализующими IDisposable, или когда идиома IDisposable (и удобство блока using) используются для чего угодно причина. - person Adam Robinson; 25.04.2012
comment
@Adam: Случай 1 как раз тот случай, когда есть неуправляемые ресурсы, которые нужно очистить. Случай 2 ортогонален моему вопросу и в любом случае не является реальным примером. - person BlueRaja - Danny Pflughoeft; 25.04.2012
comment
@ BlueRaja-DannyPflughoeft: Верно, хотя шаблон отличается в зависимости от того, используете ли вы неуправляемые ресурсы напрямую или через абстракцию. Что касается второго, то подойдет любой паттерн, в котором есть точка входа и выхода. Хотя lock уже существует, было бы легко воспроизвести такое же поведение с using. Это также может быть полезно для вещей, где вы временно меняете состояние и хотите, чтобы оно было изменено обратно ... например, при изменении курсора мыши. Вы бы изменили его в конструкторе и сбросили на то, что было в Dispose. - person Adam Robinson; 25.04.2012
comment
@BlueRaja: любому ресурсу может потребоваться детерминированное освобождение. Это может быть слот в таблице задач, огромный кусок памяти, токен в кольцевой очереди кооперативных рабочих, что угодно. - person Vlad; 26.04.2012
comment
@ BlueRaja-DannyPflughoeft: Я бы хотел, чтобы люди говорили о неуправляемых ресурсах, таких как файлы и т. Д., Поскольку возможно иметь вещи полностью в пределах вселенной управляемого кода, которые нуждаются в детерминированной очистке. Такие вещи, как блокировки и сильные события, - это ресурсы, для которых требуется очистка не на основе GC, но они не имеют доступа ни к чему за пределами управляемой платформы. - person supercat; 27.10.2015
comment
@supercat: снятие блокировки и восстановление ее ресурсов - это не одно и то же. Ни замки, ни сильные события не требуют детерминированного разрушения. - person BlueRaja - Danny Pflughoeft; 27.10.2015
comment
@ BlueRaja-DannyPflughoeft: Эксклюзивный доступ к заблокированному объекту является ресурсом; если объект, имеющий право на эксклюзивный доступ, будет оставлен без уведомления охраняемого объекта о том, что эксклюзивность больше не требуется, то никто больше не сможет использовать этот объект. Что касается событий, если неограниченное количество короткоживущих объектов подписывается на события от долгоживущего объекта, но вскоре после этого будут заброшены, такие объекты создадут неограниченную утечку памяти, так как они не станут подходящими для сбора в течение времени существования объекта. долгоживущий объект. - person supercat; 27.10.2015

Я думаю, что полезно думать о IDisposable с точки зрения обязанностей. Объект должен реализовать IDisposable, если он знает о чем-то, что нужно будет сделать между временем, когда он больше не нужен, и концом вселенной (и желательно как можно скорее), и если это единственный объект, обладающий как информацией, так и стимулом. сделать это. Например, объект, открывающий файл, должен следить за закрытием файла. Если объект просто исчезнет, ​​не закрывая файл, файл не может быть закрыт в разумные сроки.

Важно отметить, что даже объекты, которые взаимодействуют только со 100% управляемыми объектами, могут делать вещи, которые необходимо очистить (и должны использовать IDisposable). Например, IEnumerator, который присоединяется к "измененному" событию коллекции, должен будет отсоединиться, когда он больше не нужен. В противном случае, если перечислитель не использует сложную уловку, перечислитель никогда не будет собирать мусор, пока сборка находится в области видимости. Если коллекция пронумерована миллион раз, миллион перечислителей будет привязан к ее обработчику событий.

Обратите внимание, что иногда можно использовать финализаторы для очистки в тех случаях, когда по какой-либо причине объект был оставлен без Dispose первого вызова. Иногда это хорошо работает; иногда работает очень плохо. Например, даже несмотря на то, что Microsoft.VisualBasic.Collection использует финализатор для отделения счетчиков от "измененных" событий, попытка перечислить такой объект тысячи раз без вмешательства Dispose или сборки мусора приведет к его очень медленной работе - на много порядков медленнее, чем производительность, которая будет результатом правильного использования Dispose.

person supercat    schedule 25.04.2012
comment
Спасибо, я бы хотел подумать о примере IEnumerator как о реплике! - person Steve Dunn; 25.04.2012
comment
@SteveDunn: Спасибо. Похоже, широко распространено мнение, что термин «неуправляемый» во фразе «неуправляемые ресурсы» имеет какое-то отношение к неуправляемому коду. На самом деле эти две концепции в значительной степени ортогональны. Финализаторы могут несколько запутать проблему ответственности за очистку, поскольку без них язык до конца вселенной может быть несколько буквальным. Если объект без финализатора содержит единственную копию дескриптора, предоставляющего исключительный доступ к чему-либо, и он будет оставлен без освобождения дескриптора, дескриптор буквально никогда не будет освобожден. - person supercat; 25.04.2012
comment
@SteveDunn: Конечно, вопрос о том, была ли выпущена ручка когда-либо, может стать спорным задолго до конца вселенной, но ключевым моментом является то, что, как только все его копии исчезнут, ничто никогда не освободит ручку. Следовательно, последний объект с его копией должен гарантировать, что он будет выпущен, пока эта копия дескриптора все еще существует. Между прочим, еще один хороший пример неуправляемого ресурса полностью внутри управляемого кода: блокировки. - person supercat; 25.04.2012
comment
even objects which only interact with 100% unmanaged objects Разве это не должно читать 100% УПРАВЛЯЕМЫЕ объекты? В противном случае отличный ответ. Если ваша реализация владеет экземпляром, который является IDisposable, вы также должны реализовать его, чтобы очистить его, и IDisposable - единственный способ сообщить об этом. - person Andy; 25.04.2012
comment
@Andy: Исправлено. Я хочу сказать, что многие люди думают, что фраза «неуправляемый ресурс» означает ресурс, обрабатываемый собственным кодом, хотя на самом деле это не так. Хотя ресурсы, управляемые машинным кодом, почти всегда являются неуправляемыми ресурсами, они вряд ли являются единственным важным типом. На самом деле мне очень не нравятся термины «управляемые ресурсы» и «неуправляемые ресурсы», потому что они означают очень мало единообразия. Является ли то, что не управляет ничем вне себя, управляемым ресурсом, или этот термин относится только к объектам с финализаторами? - person supercat; 25.04.2012
comment
FWIW, мне неизвестны счетчики, которые отслеживают коллекцию таким образом. Например, перечислитель для List<T> просто проверяет переменную version в списке. Но, тем не менее, это верный момент. - person Adam Robinson; 25.04.2012
comment
@AdamRobinson: перечислитель класса Microsoft.VisualBasic.Collection подписывается на уведомления при изменении коллекции и использует эти уведомления, чтобы продолжать вести себя разумно. Класс Collection действительно включает логику, гарантирующую, что в случае отказа от перечислителя подписки будут очищены при следующем сборке мусора, но если кто-то перечислит Collection много (например, тысячи) раз между циклами сборки мусора, он начнет становиться очень медленный. Как только произойдет следующий сбор, он ускорится. - person supercat; 26.04.2012
comment
@supercat: Интересно! Еще одна причина избавиться от IDisposables - person Adam Robinson; 26.04.2012
comment
@AdamRobinson: Между прочим, хотя вышеупомянутый объект Collection имеет некоторые раздражающие особенности, ограничивающие его полезность для целей, отличных от исторической совместимости, он также имеет некоторые уникальные преимущества. Главный из них, это была единственная словарная коллекция, которая упростила удаление всех элементов, отвечающих определенному критерию, без необходимости создавать отдельный список элементов для удаления. - person supercat; 26.04.2012

Итак, мой вопрос: если у вас есть тип, который не содержит неуправляемых ресурсов, стоит ли внедрять IDisposable?

Когда кто-то размещает интерфейс IDisposable на объекте, это говорит мне, что создатель намеревается сделать что-то в этом методе или, возможно, в будущем намеревается это сделать. Я всегда вызываю dispose в этом случае, чтобы быть уверенным. Даже если он ничего не делает прямо сейчас, это может произойти в будущем, и отстойно получать утечку памяти, потому что они обновили объект, а вы не вызывали Dispose, когда писали код в первый раз.

По правде говоря, это призыв к суждению. Вы не хотите чрезмерно внедрять его, потому что в этот момент зачем вообще беспокоиться о сборщике мусора. Почему бы просто не утилизировать каждый объект вручную. Если есть вероятность, что вам потребуется удалить неуправляемые ресурсы, это может быть неплохой идеей. Все зависит от того, если единственные люди, использующие ваш объект, - это люди из вашей команды, вы всегда можете связаться с ними позже и сказать: «Эй, сейчас нужно использовать неуправляемый ресурс. Мы должны пройти через код и убедиться, что мы прибрались ". Если вы публикуете это для использования другими организациями, это другое. Нет простого способа сказать всем, кто, возможно, реализовал этот объект: «Эй, вам нужно убедиться, что он удален». Позвольте мне сказать вам, что есть несколько вещей, которые сводят людей с ума, чем обновление сторонней сборки, чтобы узнать, что именно они изменили свой код и заставили ваше приложение избавиться от проблем с памятью.

Мой коллега ответил, что, по сути, соединение ADO.NET является управляемым ресурсом. Я ответил на его ответ, что в конечном итоге все является неуправляемым ресурсом.

Он прав, сейчас это управляемый ресурс. Смогут ли они когда-нибудь это изменить? Кто знает, но не помешает это назвать. Я не пытаюсь догадываться о том, что делает команда ADO.NET, поэтому, если они вставят это, а он ничего не сделает, это нормально. Я все равно буду называть это, потому что одна строчка кода не повлияет на мою продуктивность.

Вы также сталкиваетесь с другим сценарием. Допустим, вы возвращаете соединение ADO.NET из метода. Вы не знаете, что соединение ADO является базовым объектом или производным типом с места в карьер. Вы не знаете, стала ли эта реализация IDisposable внезапной необходимостью. Я всегда называю это, несмотря ни на что, потому что отслеживание утечек памяти на рабочем сервере - отстой, когда он дает сбой каждые 4 часа.

person kemiller2002    schedule 25.04.2012
comment
Многие типы реализуют IDisposable не потому, что ожидают, что это может сделать будущая версия, а потому, что они или базовый тип могут использоваться в качестве возвращаемого типа для фабричных методов, которые могут возвращать производные типы, требующие очистки. Например, все типы, реализующие IEnumerator<T>, реализуют IDisposable, хотя подавляющее большинство их Dispose методов ничего не делают. - person supercat; 03.11.2015

Хотя на этот вопрос уже есть хорошие ответы, я просто хотел внести ясность.

Есть три случая реализации IDisposable:

  1. Вы напрямую используете неуправляемые ресурсы. Обычно это включает получение IntPrt или другой формы дескриптора из вызова P / Invoke, который должен быть освобожден другим вызовом P / Invoke.
  2. Вы используете другие IDisposable объекты и должны нести ответственность за их удаление
  3. У вас есть другие потребности или возможности для использования, включая удобство блока using.

Хотя я могу быть немного предвзятым, вам действительно стоит прочитать (и показать своему коллеге) StackOverflow Wiki на IDisposable.

person Adam Robinson    schedule 25.04.2012
comment
Я рекомендую обновить Wiki, включив в него управление временем жизни как причину для реализации IDisposable. Например, IObservable<T>.Subscribe возвращает IDisposable, даже если он не предназначен для инкапсуляции неуправляемых ресурсов или использования в using блоках. - person Gabe; 26.04.2012
comment
@Gabe: Это вики, так что не стесняйтесь редактировать. Раньше я не использовал IObservable<T>, так что будет лучше, если ты что-нибудь добавишь. - person Adam Robinson; 26.04.2012
comment
@AdamRobinson: Должны быть некоторые пояснения по поводу всегда звонить IDisposable. Важно, чтобы Dispose вызывался до того, как последняя ссылка на IDisposable будет уничтожена (так как он не может быть вызван после). С другой стороны, многие объекты обычно содержат ссылки на IDisposable; как правило, нужно звонить Dispose. - person supercat; 26.04.2012
comment
@supercat: Да, ты прав. Для каждого IDisposable должен быть один владелец, ответственный за обеспечение правильного вызова Dispose. - person Adam Robinson; 26.04.2012

Dispose следует использовать для любого ресурса с ограниченным сроком службы. Для любого неуправляемого ресурса следует использовать финализатор. У любого неуправляемого ресурса должно быть ограниченное время жизни, но существует множество управляемых ресурсов (например, блокировок), которые также имеют ограниченное время жизни.

person Gabe    schedule 25.04.2012

Обратите внимание, что неуправляемые ресурсы вполне могут включать стандартные объекты CLR, например, содержащиеся в некоторых статических полях, все они работают в безопасном режиме без какого-либо неуправляемого импорта.

Нет простого способа узнать, действительно ли данному классу, реализующему IDiposable, нужно что-то очищать. Мое практическое правило - всегда вызывать Dispose для объектов, которые я не слишком хорошо знаю, например для какой-нибудь сторонней библиотеки.

person Jacek Gorgoń    schedule 25.04.2012
comment
Стоит отметить, что если бы даже у объекта было свойство DisposeRequired, время, необходимое для выяснения необходимости Dispose, (немного) превысило бы время, необходимое для безусловного вызова Dispose [так как он должен был бы сделать виртуальный вызов свойства и затем переходите к результату вместо простого виртуального вызова]. Единственный раз, когда DisposeRequired был бы полезен, было бы, если бы определение когда для вызова Dispose было бы более обременительным, чем его фактический вызов (например, если потребуется подсчет пользователей для объектов, требующих очистки, но не для тех, которые не т). - person supercat; 26.12.2013

Нет, это не только для неуправляемых ресурсов.

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

person Tigran    schedule 25.04.2012
comment
Реализация (и вызов) Dispose критически важна для большинства классов, хранящих ресурсы. управляемый / неуправляемый в основном не имеет значения. - person Henk Holterman; 26.04.2012
comment
@HenkHolterman: мне кажется, что я именно имел в виду: это касается не только неуправляемого управления ресурсами. Не так ли? - person Tigran; 26.04.2012
comment
Да, прости. Я пропустил там не. - person Henk Holterman; 26.04.2012

Если вы объединяете IDisposables, вам следует реализовать интерфейс, чтобы своевременно очищать эти элементы. Как еще myConn.Dispose() будет вызываться в приведенном вами примере подключения ADO.Net?

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

person Steve Townsend    schedule 25.04.2012

Ты прав. Управляемые соединения с базой данных, файлы, ключи реестра, сокеты и т. Д. - все это относится к неуправляемым объектам. Вот почему они реализуют IDisposable. Если у вашего типа есть одноразовые объекты, вы должны реализовать IDisposable и избавиться от них в своем Dispose методе. В противном случае они могут оставаться в живых до тех пор, пока не будет собран мусор, что приведет к заблокированным файлам и другому неожиданному поведению.

person Martin Liversage    schedule 25.04.2012
comment
Э, похоже, вы согласны с коллегой OP, а не с OP. - person BlueRaja - Danny Pflughoeft; 25.04.2012
comment
Что ж, я предполагаю, что коллега утверждает, что если у вашего типа есть управляемое соединение ADO, ему не нужно реализовывать IDisposable, потому что ресурс является управляемым. Я говорю, что вы должны это сделать, потому что глубоко внутри любого IDisposable объекта находится неуправляемый ресурс. При агрегировании IDisposable объектов вам также необходимо реализовать IDisposable. - person Martin Liversage; 25.04.2012
comment
В этом случае необходимо очистить неуправляемые ресурсы. Коллега утверждает, что если нет неуправляемых ресурсов, нет необходимости в IDisposable - OP, похоже, думает, что есть смысл в реализации IDisposable [..], даже если нет неуправляемых ресурсов, которые нужно очистить. - person BlueRaja - Danny Pflughoeft; 25.04.2012
comment
@ BlueRaja-DannyPflughoeft: Думаю, вопрос несколько неоднозначный. Мой вопрос был в основном ответом на Мой ответ на его ответ заключался в том, что в конечном итоге все является неуправляемым ресурсом. Я поддерживаю это утверждение. - person Martin Liversage; 25.04.2012

в конечном итоге все является неуправляемым ресурсом.

Не правда. Все, кроме памяти, используемой объектами CLR, которая управляется (выделяется и освобождается) только структурой.

Внедрение IDisposable и вызов Dispose для объекта, который не удерживает какие-либо неуправляемые ресурсы (прямо или косвенно через зависимые объекты), бессмысленно. Это не делает освобождение этого объекта детерминированным, потому что вы не можете напрямую освободить память CLR объекта самостоятельно, поскольку всегда только GC тот. Объект является ссылочным типом, поскольку типы значений при использовании непосредственно на уровне метода выделяются / освобождаются операциями стека.

Теперь все утверждают, что правы в своих ответах. Позвольте мне доказать свое. Согласно документации:

Метод Object.Finalize позволяет объекту попытаться освободить ресурсы и выполнить другие операции очистки до того, как он будет восстановлен сборкой мусора.

Другими словами, память CLR объекта освобождается сразу после вызова Object.Finalize(). [примечание: при необходимости можно явно пропустить этот вызов]

Вот одноразовый класс без неуправляемых ресурсов:

internal class Class1 : IDisposable
{
    public Class1()
    {
        Console.WriteLine("Construct");
    }

    public void Dispose()
    {
        Console.WriteLine("Dispose");
    }

    ~Class1()
    {
        Console.WriteLine("Destruct");
    }
}

Обратите внимание, что деструктор неявно вызывает каждый Finalize в цепочке наследования до Object.Finalize()

А вот метод Main консольного приложения:

static void Main(string[] args)
{
    for (int i = 0; i < 10; i++)
    {
        Class1 obj = new Class1();
        obj.Dispose();
    }

    Console.ReadKey();
}

Если бы вызов Dispose был способом освободить управляемый объект детерминированным способом, за каждым «Dispose» сразу следовало бы «Destruct», верно? Смотрите сами, что происходит. Это приложение наиболее интересно запускать из окна командной строки.

Примечание. Существует способ заставить GC собирать все объекты, ожидающие завершения в текущем домене приложения, но не для одного конкретного объекта. Тем не менее, вам не нужно вызывать Dispose, чтобы объект был в очереди финализации. Настоятельно не рекомендуется принудительно собирать данные, поскольку это, скорее всего, снизит общую производительность приложения.

ИЗМЕНИТЬ

Есть одно исключение - государственное управление. Dispose может обрабатывать изменение состояния, если ваш объект управляет внешним состоянием. Даже если состояние не является неуправляемым объектом, его очень удобно использовать как один из-за особой обработки IDisposable. Примером может быть контекст безопасности или контекст олицетворения.

using (WindowsImpersonationContext context = SomeUserIdentity.Impersonate()))
{
    // do something as SomeUser
}

// back to your user

Это не лучший пример, потому что WindowsImpersonationContext внутренне использует системный дескриптор, но вы понимаете.

Суть в том, что при реализации IDisposable вам нужно иметь (или планировать иметь) что-то значимое для выполнения в методе Dispose. В противном случае это просто пустая трата времени. IDisposable не меняет способ управления вашим объектом с помощью GC.

person Community    schedule 01.05.2012
comment
Вы здесь не правы. Объект может физически не собираться мусором, но логически он освободит что-то важное в известное время. Это может быть не только какой-то неуправляемый ресурс, но также слот в очереди, выделение потока пула потоков для некоторых вычислений, лицензия на использование какой-либо другой программы и т. Д. - то есть, логический ресурс. Наличие объекта в памяти - это чистая деталь, и ее не следует учитывать, если одноразовый объект реализован правильно. - person Vlad; 02.05.2012
comment
@Vlad Вы пропустили часть моего ответа. Все, что вы упомянули, в конечном итоге является неуправляемыми ресурсами. Если ваш объект имеет дело с ними (прямо или косвенно через зависимые объекты), он становится неуправляемым и должен реализовывать Dispose. Другими словами, если какой-либо зависимый объект реализует Dispose, ваш объект также должен. Извините, если это не было четко указано. - person Maciej; 02.05.2012
comment
Что ж, я не подвергаю сомнению весь ваш ответ, это просто часть, вызывающая Dispose для объекта, который не удерживает какие-либо неуправляемые ресурсы (...), бессмысленна. Я попытался получить несколько примеров, в которых ресурс фактически управляется, хотя его нужно освобождать детерминированным способом. Например, право на изменение коллекции явно не является неуправляемым ресурсом, но получение этого права в RAII-way с IDisposable - это хорошо: using (ObtainModifyRight(collection)) { /* modify it */ }. - person Vlad; 02.05.2012
comment
Управление состоянием с помощью Dispose - это своего рода перехват шаблона удаления, поскольку он предназначен для неуправляемых ресурсов. Но я согласен, что это иногда случается, и это удобно из-за ключевого слова using. Обновление ответа. - person Maciej; 02.05.2012
comment
Право на изменение коллекции является неуправляемым ресурсом, если предоставление такого права одному объекту ухудшит способность других объектов изменять эту коллекцию до тех пор, пока объект, получивший такое право, не укажет, что он больше не нужен. В конце концов, что на самом деле означает для объекта запрос блока неуправляемой памяти у операционной системы, за исключением того, что (1) ОС дает объекту право использовать эту память и (2) никто другой не сможет использовать его, пока первый объект не скажет ОС, что он больше не нужен? - person supercat; 19.06.2012
comment
@Maciej - что касается бессмысленного комментария, возможно, вам стоит прочитать другие мои объяснения к другим сообщениям, на которые я связал свой ответ. Что касается доказательства вашего ответа, документация - это одно, но если вы не отлаживали дампы памяти исключений OOM с помощью WinDbg и не обладаете знаниями о внутренней работе сборщика мусора, вам следует полагаться на опыт больше, чем на документацию. - person Dave Black; 05.09.2012

Ваш Тип должен реализовывать IDisposable, если он ссылается на неуправляемые ресурсы или если он содержит ссылки на объекты, реализующие IDisposable.

person justin.m.chase    schedule 25.04.2012

В одном из моих проектов у меня был класс с управляемыми потоками внутри него, мы назовем их потоком A и потоком B, а также объектом IDisposable, назовем его C.

Используется для удаления C при выходе. B использовал C для сохранения исключений.

Мой класс должен был реализовать IDisposable и descrtuctor, чтобы гарантировать, что все утилизируется в правильном порядке. Да, GC мог очистить мои предметы, но, по моему опыту, было состояние гонки, если мне не удалось очистить мой класс.

person M Afifi    schedule 18.05.2012

Краткий ответ: Абсолютно НЕТ. Если у вашего типа есть управляемые или неуправляемые члены, вам следует реализовать IDisposable.

Теперь подробности: я ответил на этот вопрос и предоставил гораздо больше подробностей о внутреннем устройстве управления памятью и GC по вопросам здесь, в StackOverflow. Здесь только несколько:

Что касается лучших практик по реализации IDisposable, пожалуйста, обратитесь к моему сообщению в блоге:

Как правильно реализовать IDisposable узор?

person Dave Black    schedule 31.08.2012

Совершенно не нужны ресурсы (управляемые или неуправляемые). Часто IDisposable - это всего лишь удобный способ избавиться от громоздкости try {..} finally {..}, просто сравните:

  Cursor savedCursor = Cursor.Current;

  try {
    Cursor.Current = Cursors.WaitCursor;

    SomeLongOperation();
  }
  finally {
    Cursor.Current = savedCursor;
  }

с участием

  using (new WaitCursor()) {
    SomeLongOperation();
  }

где WaitCursor означает IDisposable, чтобы соответствовать using:

  public sealed class WaitCursor: IDisposable {
    private Cursor m_Saved;

    public Boolean Disposed {
      get;
      private set;
    }

    public WaitCursor() {
      Cursor m_Saved = Cursor.Current;
      Cursor.Current = Cursors.WaitCursor;
    }

    public void Dispose() {
      if (!Disposed) {
        Disposed = true;
        Cursor.Current = m_Saved;
      }
    }
  }

Вы можете легко комбинировать такие классы:

  using (new WaitCursor()) {
    using (new RegisterServerLongOperation("My Long DB Operation")) {
      SomeLongRdbmsOperation();  
    }

    SomeLongOperation();
  }
person Dmitry Bychenko    schedule 26.10.2015

Реализуйте IDisposable, если объекту принадлежат какие-либо неуправляемые объекты или какие-либо управляемые одноразовые объекты

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

person Igor Pashchuk    schedule 01.08.2015