Деструктор против IDisposable?

Я читал об удалении объектов /IDisposable интерфейса и деструкторов в С#, но для меня они, похоже, делают то же самое?

В чем разница между этими двумя? Почему я должен использовать один над другим? Фактически, в этом примере (ссылка ниже) этот код использует как интерфейс IDisposable, так и деструктор:

http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

В комментарии говорится, что деструктор - это если код завершения не используется, но как я могу решить, когда использовать его над другим?

Ответ 1

Я написал довольно подробный пост, который должен помочь объяснить о финализаторах, IDisposable и когда вы должны использовать тот или иной: http://gregbee.ch/blog/implementing-and-using-the-idisposable-interface

Вероятно, наиболее важная часть приведена ниже:

Когда вы используете неуправляемые ресурсы таких как ручки и база данных соединений, вы должны убедиться, что они удерживаются за минимальную сумму времени, используя принцип запоздать и освобождать рано. В С++ освобождение ресурсов обычно сделано в деструкторе, которое детерминированно выполняются в точке где объект удален. Сеть Однако время выполнения использует мусор сборщик (GC) для очистки и утилизации память, используемая объектами, которые не являются более достижимый; поскольку это выполняется на периодической основе это означает, что точка при котором ваш объект очищается, недетерминирован. Следствие это то, что деструкторы не существуют для управляемых объектов, поскольку нет детерминированное место для их запуска.

Вместо деструкторов С# имеет финализаторы, которые реализуются переопределение метода Finalize на базовом классе Object (хотя С# несколько смутно использует С++ синтаксис деструктора ~ Объект для этого). Если объект переопределяет Finalize затем вместо собранный GC, когда он отсутствует scope, GC помещает его в финализатор очередь. В следующем цикле GC все финализаторы в очереди запускаются (на один поток в текущем реализация) и память из завершенные объекты возвращены. Это довольно очевидно из этого, почему вы не хотите сделать очистку в финализаторе: это требуется два цикла GC для сбора объект вместо одного, и есть одна нить, где все финализаторы выполняются, пока каждый другой поток приостановлено, так что это будет больно производительность.

Итак, если у вас нет деструкторов и вы не хотите оставлять очистку до финализатор, тогда единственным вариантом является ручным, детерминированным, чистым вверх по объекту. Введите IDisposable интерфейс, который обеспечивает стандарт для поддержки этой функциональности и определяет один метод: Dispose, где вы ввели логику очистки для объект. При использовании в конечном итоге блок, этот интерфейс обеспечивает эквивалентную функциональность деструкторов. Причина, в конце концов, блоки в коде в первую очередь поддерживают интерфейс IDisposable; вот почему С++ использует просто try/except, поскольку есть нет необходимости в конечном блоке с деструкторов.

Ответ 2

Краткая версия

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

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


Мой fooobar.com/questions/1025/... предлагает вам с самого начала, почему у вас есть IDisposable, что он должен делать, что может сделать ваш финализатор, что он не должен делать.

Этот ответ плавит лица

был использован для описания: P

Ответ 3

Наличие деструктора (~ Object()) в управляемом языке программирования - самая дурная идея. Для неуправляемых языков, таких как C, С++, совершенно разумно иметь деструкторы, поскольку они используют идиому RAII, но для управляемых, таких как Java, С#, просто абсурдно.

Как отметил Джошуа Блох (Joshua Bloch), бывший проект в Java Collection Framework, идея метода finalize(), эквивалентная С# С++ как деструктор) на Java, была самой большой ошибкой, когда-либо сделанной. То же, что и в С#, finallize() в Java дает накладные расходы "новым", так как он должен быть добавлен в очередь финализатора при распределении. Более того, сборщик мусора должен появиться и запустить finallize() в очереди, так что дважды накладные расходы во время gc.

У С# было множество расширенных функций, таких как "использование (IDisposable) {}", которые не только позволяют ограничивать IDisposable-переменную в области "использования", но и гарантировать ее очистку. Мой вопрос: почему С# следовали одному и тому же пути Java, что привело к большой ошибке. Может быть, если развитие dotnet началось после 2003 ~ 2005, когда многие архитекторы Java обнаружили ошибку finallize(), тогда ошибка была бы предотвращена.

Многие хорошие идеи одного языка часто переносятся на другой язык, такой как "IDisposable/using combo" в С#, который был перенесен на Java 1.7 в свой оператор try (object-to-dispose) {} ". Но его слишком плохо, что языковые архитекторы не могут обнаружить плохую идею, замаскированную под хорошую идею во время ее передачи от одного к другому.

Мой совет никогда не должен использовать ~ Destructor() и придерживаться IDisposable/using combo, если вам нужно вручную очистить неуправляемый ресурс, например, соединения с базой данных.