Разрушитель против IDisposable? - PullRequest
62 голосов
/ 19 января 2009

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

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

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

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

Ответы [ 3 ]

81 голосов
/ 19 января 2009

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

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

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

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

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

21 голосов
/ 22 июля 2011

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

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

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


Мой самый популярный ответ по Stackoverflow с самого начала объясняет, почему у вас есть IDisposable, что он должен делать, что может делать ваш финализатор, чего он не должен.

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

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

5 голосов
/ 22 ноября 2012

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...