.NET IDisposable Pattern подразумевает, что если вы пишете финализатор и внедряете IDisposable, ваш финализатор должен явно вызывать Dispose. Это логично, и это то, что я всегда делал в тех редких ситуациях, когда требуется финализатор.
Однако, что произойдет, если я просто сделаю это:
class Foo : IDisposable
{
public void Dispose(){ CloseSomeHandle(); }
}
и не реализовывать финализатор или что-то еще. Будет ли фреймворк вызывать метод Dispose для меня?
Да, я понимаю, что это звучит глупо, и вся логика подразумевает, что этого не произойдет, но у меня всегда было 2 вещи в затылке, которые заставляли меня неуверенно себя чувствовать.
Кто-то несколько лет назад однажды сказал мне, что он на самом деле это сделает, и у этого человека был очень солидный послужной список "знания своих вещей".
Компилятор / фреймворк выполняет другие «волшебные» вещи в зависимости от того, какие интерфейсы вы реализуете (например, foreach, методы расширения, сериализация на основе атрибутов и т. Д.), Поэтому имеет смысл, что это тоже может быть «магическим».
Несмотря на то, что я прочитал много материала об этом, и многое подразумевалось, я так и не смог найти окончательного ответа «да» или «нет» на этот вопрос.
источник
Я хочу подчеркнуть точку зрения Брайана в его комментарии, потому что это важно.
Финализаторы не являются детерминированными деструкторами, как в C ++. Как уже отмечали другие, нет гарантии того, когда он будет вызван, и действительно, если у вас достаточно памяти, будет ли он когда-либо вызван.
Но плохая вещь в финализаторах заключается в том, что, как сказал Брайан, это заставляет ваш объект переживать сборку мусора. Это может быть плохо. Зачем?
Как вы можете знать, а может и не знать, GC разделен на поколения - Gen 0, 1 и 2 плюс куча больших объектов. Разделение - это бесполезный термин - вы получаете один блок памяти, но есть указатели того, где объекты Gen 0 начинаются и заканчиваются.
Мысленный процесс состоит в том, что вы, вероятно, будете использовать множество объектов, которые будут недолговечны. Так что GC должен легко и быстро добраться до объектов Gen 0. Поэтому, когда возникает нехватка памяти, первое, что он делает, это коллекция Gen 0.
Теперь, если это не устраняет достаточное давление, он возвращается и выполняет развертку 1-го поколения (переделывает Gen 0), а затем, если все еще недостаточно, он выполняет развертку 2-го поколения (повторяет Gen 1 и Gen 0). Таким образом, очистка долгоживущих объектов может занять некоторое время и быть довольно дорогой (поскольку ваши потоки могут быть приостановлены во время операции).
Это означает, что если вы делаете что-то вроде этого:
Ваш объект, несмотря ни на что, доживет до поколения 2. Это потому, что GC не может вызвать финализатор во время сборки мусора. Таким образом, объекты, которые должны быть завершены, перемещаются в специальную очередь, которая будет очищена другим потоком (потоком финализатора - который, если вы убьете, приведет к возникновению всевозможных плохих вещей). Это означает, что ваши объекты задерживаются дольше и потенциально вызывают больше мусора.
Таким образом, все это просто указывает на то, что вы хотите использовать IDisposable для очистки ресурсов, когда это возможно, и серьезно пытаться найти способы обойти использование финализатора. Это в интересах вашего приложения.
источник
Здесь уже есть много хороших дискуссий, и я немного опоздал на вечеринку, но я сам хотел добавить несколько моментов.
Это простая версия, но есть много нюансов, которые могут запутать вас в этом паттерне.
По моему мнению, намного лучше полностью избегать использования типов, которые содержат как одноразовые ссылки, так и собственные ресурсы, которые могут потребовать доработки. SafeHandles предоставляют очень чистый способ сделать это, инкапсулируя собственные ресурсы в одноразовые, которые внутренне обеспечивают их собственную финализацию (наряду с рядом других преимуществ, таких как удаление окна во время P / Invoke, где собственный дескриптор может быть потерян из-за асинхронного исключения) ,
Простое определение SafeHandle делает это тривиальным:
Позволяет упростить содержащий тип:
источник
GC.SuppressFinalize
в этом примере. В этом контексте SuppressFinalize следует вызывать только в случаеDispose(true)
успешного выполнения. ЕслиDispose(true)
в какой-то момент происходит сбой после подавления финализации, но до очистки всех ресурсов (особенно неуправляемых), вам все равно нужно выполнить финализацию, чтобы выполнить как можно больше очистки. Лучше переместитьGC.SuppressFinalize
вызов вDispose()
метод после вызоваDispose(true)
. См. Руководство по разработке фреймворка и этот пост .Я так не думаю. Вы можете контролировать вызов Dispose, что означает, что вы можете теоретически написать код удаления, который делает предположения о (например) существовании других объектов. Вы не можете контролировать, когда вызывается финализатор, поэтому было бы неправильно, чтобы финализатор автоматически вызывал Dispose от вашего имени.
РЕДАКТИРОВАТЬ: Я ушел и проверил, просто чтобы убедиться:
источник
Не в том случае, который вы описываете, но GC позвонит вам в финализатор , если он у вас есть.
ТЕМ НЕ МЕНИЕ. Следующая сборка мусора, вместо того, чтобы собираться, объект попадает в очередь финализации, все собирается, затем вызывается финализатор. Следующая коллекция после этого будет освобождена.
В зависимости от нагрузки на память вашего приложения, у вас может не быть gc для генерации этого объекта некоторое время. Таким образом, в случае, скажем, файлового потока или соединения БД, вам, возможно, придется подождать некоторое время, пока неуправляемый ресурс на некоторое время будет освобожден в вызове финализатора, что вызовет некоторые проблемы.
источник
Нет, это не называется.
Но это позволяет легко не забыть избавиться от своих предметов. Просто используйте
using
ключевое слово.Я сделал следующий тест для этого:
источник
GC не будет звонить утилизировать. Он может вызвать ваш финализатор, но даже это не гарантируется при любых обстоятельствах.
Смотрите эту статью для обсуждения лучшего способа справиться с этим.
источник
Документация по IDisposable дает довольно четкое и подробное объяснение поведения, а также пример кода. GC НЕ вызовет
Dispose()
метод интерфейса, но вызовет финализатор для вашего объекта.источник
Шаблон IDisposable был создан в первую очередь для вызова разработчиком, если у вас есть объект, который реализует IDispose, разработчик должен либо реализовать
using
ключевое слово вокруг контекста объекта, либо напрямую вызвать метод Dispose.Отказоустойчивым для шаблона является реализация финализатора, вызывающего метод Dispose (). Если вы этого не сделаете, вы можете создать некоторые утечки памяти, например: Если вы создаете какую-либо оболочку COM и никогда не вызываете System.Runtime.Interop.Marshall.ReleaseComObject (comObject) (который будет помещен в метод Dispose).
В clr нет магии для автоматического вызова методов Dispose, кроме отслеживания объектов, содержащих финализаторы, и их сохранения в таблице финализаторов GC и вызова их, когда GC запускает некоторую очистку эвристики.
источник