Завершить против утилизации
Почему некоторые люди используют Finalize
метод над Dispose
метод?
В каких ситуациях вы бы использовали Finalize
метод над Dispose
метод и наоборот?
16 ответов
Другие уже покрыли разницу между Dispose
а также Finalize
(кстати Finalize
метод до сих пор называется деструктором в спецификации языка), поэтому я просто добавлю немного о сценариях, где Finalize
Метод пригодится.
Некоторые типы инкапсулируют одноразовые ресурсы таким образом, чтобы их было легко использовать и избавляться от них одним действием. Общее использование часто так: открыть, прочитать или написать, закрыть (удалить). Это очень хорошо сочетается с using
построить.
Другие немного сложнее. WaitEventHandles
поскольку экземпляры не используются, как это, поскольку они используются для передачи сигналов от одного потока к другому. Тогда возникает вопрос: кому звонить? Dispose
на этих? В качестве защитных типов, подобных этим, реализуют Finalize
метод, который обеспечивает удаление ресурсов, когда приложение больше не ссылается на экземпляр.
Метод финализатора вызывается, когда ваш объект собирается мусором, и у вас нет гарантии, когда это произойдет (вы можете принудительно вызвать его, но это снизит производительность).
Dispose
метод, с другой стороны, предназначен для вызова кодом, который создал ваш класс, чтобы вы могли очистить и освободить любые полученные вами ресурсы (неуправляемые данные, соединения с базой данных, файловые дескрипторы и т. д.) в тот момент, когда код выполняется с вашим объект.
Стандартная практика заключается в реализации IDisposable
а также Dispose
так что вы можете использовать свой объект в using
Постулаты. Такие как using(var foo = new MyObject()) { }
, И в свой финализатор вы звоните Dispose
На всякий случай телефонный код забыл избавиться от вас.
Finalize - это метод backstop, вызываемый сборщиком мусора, когда он возвращает объект. Dispose - это метод "детерминированной очистки", вызываемый приложениями для освобождения ценных собственных ресурсов (дескрипторов окон, соединений с базами данных и т. Д.), Когда они больше не нужны, вместо того, чтобы оставлять их удерживаемыми на неопределенный срок до тех пор, пока GC не вернется к объекту.
Как пользователь объекта, вы всегда используете Dispose. Финализация для GC.
Как разработчик класса, если вы располагаете управляемыми ресурсами, которые должны быть утилизированы, вы реализуете Dispose. Если у вас есть собственные ресурсы, вы реализуете и Dispose и Finalize, и оба вызываете общий метод, который освобождает собственные ресурсы. Эти идиомы обычно объединяются с помощью частного метода Dispose(bool dispose), который Dispose вызывает с помощью true и завершает вызовы с помощью false. Этот метод всегда освобождает собственные ресурсы, затем проверяет параметр распоряжения, и если он имеет значение true, он удаляет управляемые ресурсы и вызывает GC.SuppressFinalize.
Finalize вызывается GC, когда этот объект больше не используется.
Dispose - это обычный метод, который пользователь этого класса может вызвать для освобождения любых ресурсов.
Если пользователь забыл вызвать Dispose и если в классе реализован Finalize, то GC удостоверится, что он вызван.
завершать
- Финализаторы всегда должны быть
protected
неpublic
или жеprivate
так что метод не может быть вызван из кода приложения напрямую, и в то же время он может сделать вызовbase.Finalize
метод - Финализаторы должны освобождать только неуправляемые ресурсы.
- Фреймворк не гарантирует, что финализатор будет выполняться вообще в любом конкретном случае.
- Никогда не выделяйте память в финализаторах и не вызывайте виртуальные методы из финализаторов.
- Избегайте синхронизации и создания необработанных исключений в финализаторах.
- Порядок выполнения финализаторов недетерминирован - иными словами, вы не можете полагаться на другой объект, все еще доступный в вашем финализаторе.
- Не определяйте финализаторы для типов значений.
- Не создавайте пустых деструкторов. Другими словами, вы никогда не должны явно определять деструктор, если вашему классу не нужно очищать неуправляемые ресурсы, и если вы его определите, он должен выполнить некоторую работу. Если позже вам больше не нужно очищать неуправляемые ресурсы в деструкторе, удалите его полностью.
избавиться
- Воплощать в жизнь
IDisposable
на каждый тип, который имеет финализатор - Убедитесь, что объект стал непригодным для использования после вызова
Dispose
метод. Другими словами, избегайте использования объекта послеDispose
метод был вызван на это. - Вызов
Dispose
на всеIDisposable
Типы, как только вы закончите с ними - Разрешать
Dispose
быть вызванным несколько раз без возникновения ошибок. - Подавить последующие вызовы финализатора из
Dispose
метод с использованиемGC.SuppressFinalize
метод - Избегайте создания одноразовых типов стоимости
- Избегайте бросать исключения изнутри
Dispose
методы
Утилизировать / Завершить шаблон
- Microsoft рекомендует вам реализовать оба
Dispose
а такжеFinalize
при работе с неуправляемыми ресурсами.Finalize
Реализация будет запущена, и ресурсы будут по-прежнему освобождаться, когда объект будет собирать мусор, даже если разработчик забыл вызватьDispose
метод явно. - Очистить неуправляемые ресурсы в
Finalize
метод, а такжеDispose
метод. Дополнительно позвонитеDispose
метод для любых объектов.NET, которые у вас есть в качестве компонентов внутри этого класса (с неуправляемыми ресурсами в качестве их члена) изDispose
метод.
Вот некоторые ключи из книги MCSD Certification Toolkit (экзамен 70-483), стр. 193:
деструктор ≈(он почти равен) base.Finalize (), деструктор преобразуется в переопределенную версию метода Finalize, который выполняет код деструктора и затем вызывает метод Finalize базового класса. Тогда это полностью недетерминированный, вы не можете знать, когда будет вызван, потому что зависит от GC.
Если класс не содержит управляемых ресурсов и неуправляемых ресурсов, ему не нужно реализовывать в IDisposableor деструктор.
Если класс имеет только управляемые ресурсы, он должен реализовывать IDisposable, но ему не нужен деструктор. (Когда деструктор выполняется, вы не можете быть уверены, что управляемые объекты все еще существуют, поэтому вы все равно не можете вызывать их методы Dispose.)
Если у класса есть только неуправляемые ресурсы, он должен реализовать IDisposable и деструктор, если программа не вызывает Dispose.
Метод Dispose должен быть безопасным для запуска более одного раза. Вы можете достичь этого, используя переменную, чтобы отслеживать, была ли она запущена ранее.
Метод Dispose должен освободить как управляемые, так и неуправляемые ресурсы.
Деструктор должен освобождать только неуправляемые ресурсы. (Когда деструктор выполняется, вы не можете быть уверены, что управляемые объекты все еще существуют, поэтому вы все равно не можете вызывать их методы Dispose.)
После освобождения ресурсов деструктор должен вызвать GC.SuppressFinalize, чтобы объект мог пропустить очередь завершения.
Пример реализации для класса с неуправляемыми и управляемыми ресурсами:
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
}
}
99% времени вам не нужно беспокоиться ни о чем.:) Но, если ваши объекты содержат ссылки на неуправляемые ресурсы (например, дескрипторы окон, дескрипторы файлов), вам необходимо предоставить способ для управляемого объекта освободить эти ресурсы. Finalize дает неявный контроль над высвобождением ресурсов. Вызывается сборщиком мусора. Dispose - это способ явно контролировать освобождение ресурсов и может вызываться напрямую.
Существует гораздо больше информации о сборке мусора, но это только начало.
Финализатор предназначен для неявной очистки - вы должны использовать это всякий раз, когда класс управляет ресурсами, которые абсолютно необходимо очистить, так как в противном случае вы бы потеряли дескрипторы / память и т. Д.
Корректная реализация финализатора общеизвестно трудна, и ее следует избегать везде, где это возможно - SafeHandle
class (доступен в.Net v2.0 и выше) теперь означает, что вам очень редко (если вообще когда-либо) нужно реализовывать финализатор.
IDisposable
Интерфейс предназначен для явной очистки и используется гораздо чаще - вы должны использовать это, чтобы позволить пользователям явно освобождать или очищать ресурсы всякий раз, когда они закончили использовать объект.
Обратите внимание, что если у вас есть финализатор, вы должны также реализовать IDisposable
интерфейс, позволяющий пользователям явно освобождать эти ресурсы раньше, чем они были бы, если бы объект собирался мусором.
См. DG Update: Dispose, Finalization и Resource Management для того, что я считаю лучшим и наиболее полным набором рекомендаций по финализаторам и IDisposable
,
Основное различие между Dispose и Finalize заключается в следующем:
Dispose
обычно вызывается вашим кодом. Ресурсы высвобождаются мгновенно, когда вы это вызываете. Люди забывают вызвать метод, поэтомуusing() {}
заявление придумано. Когда ваша программа завершит выполнение кода внутри{}
, он позвонит Dispose
метод автоматически.
Finalize
не вызывается вашим кодом. Это означает, что он вызывается сборщиком мусора (GC). Это означает, что ресурс может быть освобожден в любое время в будущем, когда сборщик мусора решит это сделать. Когда GC сделает свою работу, он будет использовать множество методов Finalize. Если в этом есть тяжелая логика, это замедлит процесс. Это может вызвать проблемы с производительностью вашей программы. Так что будьте осторожны с тем, что вы туда вкладываете.
Я лично написал бы большую часть логики уничтожения в Dispose. Надеюсь, это устранит путаницу.
Различаются методы Finalize и Dispose в C#.
GC вызывает метод finalize для восстановления неуправляемых ресурсов (таких как управление файлами, api windows, сетевое подключение, подключение к базе данных), но время не фиксируется, когда GC вызывает его. Он неявно вызывается GC, это означает, что у нас нет контроля низкого уровня.
Метод Dispose: у нас низкий уровень контроля над ним, как мы его называем из кода. мы можем вернуть неуправляемые ресурсы всякий раз, когда считаем, что они непригодны для использования. Мы можем добиться этого путем внедрения шаблона IDisposal.
Резюме -
- Вы пишете финализатор для своего класса, если он имеет ссылку на неуправляемые ресурсы, и вы хотите убедиться, что эти неуправляемые ресурсы освобождаются, когда экземпляр этого класса собираетсяавтоматически. Обратите внимание, что вы не можете вызывать Finalizer объекта явно - он вызывается сборщиком мусора автоматически по мере необходимости.
- С другой стороны, вы реализуете интерфейс IDisposable (и, следовательно, определяете метод Dispose() как результат для вашего класса), когда у вашего класса есть ссылка на неуправляемые ресурсы, но вы не хотите ждать, пока сборщик мусора вступит в действие (который может быть в любое время - не под контролем программиста) и хочет освободить эти ресурсы, как только вы закончите. Таким образом, вы можете явно освобождать неуправляемые ресурсы, вызывая метод Dispose() объекта.
Кроме того, еще одно отличие состоит в том, что в реализации Dispose() вы также должны освобождать управляемые ресурсы, тогда как в Finalizer это делать не следует. Это связано с тем, что весьма вероятно, что управляемые ресурсы, на которые ссылается объект, уже очищены до того, как будут готовы к финализации.
Для класса, который использует неуправляемые ресурсы, рекомендуется определить оба метода - Dispose() и Finalizer - для использования в качестве запасного варианта на случай, если разработчик забудет явно избавиться от объекта. Оба могут использовать общий метод для очистки управляемых и неуправляемых ресурсов:
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
Лучший пример, который я знаю.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
Экземпляры класса часто инкапсулируют контроль над ресурсами, которые не управляются средой выполнения, такими как дескрипторы окон (HWND), соединения с базой данных и так далее. Поэтому вы должны предоставить как явный, так и неявный способ освобождения этих ресурсов. Обеспечьте неявное управление, реализовав защищенный метод Finalize для объекта (синтаксис деструктора в C# и управляемые расширения для C++). Сборщик мусора вызывает этот метод в какой-то момент после того, как больше нет допустимых ссылок на объект. В некоторых случаях вы можете захотеть предоставить программистам, использующим объект, возможность явно освобождать эти внешние ресурсы до того, как сборщик мусора освободит объект. Если внешний ресурс является дефицитным или дорогим, можно добиться большей производительности, если программист явно освобождает ресурсы, когда они больше не используются. Чтобы обеспечить явное управление, реализуйте метод Dispose, предоставляемый интерфейсом IDisposable. Потребитель объекта должен вызывать этот метод, когда это делается с использованием объекта. Dispose может быть вызван, даже если другие ссылки на объект живы.
Обратите внимание, что даже когда вы предоставляете явное управление с помощью Dispose, вы должны обеспечивать неявную очистку с помощью метода Finalize. Finalize обеспечивает резервное копирование для предотвращения постоянной утечки ресурсов, если программист не может вызвать Dispose.
Я сегодня много искал ответ на этот вопрос. Я поделюсь своими знаниями здесь. Мой ответ основан на , потому что у нее есть самое ясное объяснение, которое я видел.
Когда у ваших объектов есть доступ к неуправляемым ресурсам, вы должны вручную освободить эти ресурсы. Это можно сделать с помощью IDisposable или finalizer, что означает, что они оба освобождают неуправляемые ресурсы.
Практическое правило: реализуйте IDisposable для освобождения неуправляемых ресурсов, а код вызывающего абонента должен вызывать метод Dispose. Если вызывающий абонент забывает вызвать метод Dispose(), вы все равно можете предоставить метод для освобождения этих неуправляемых ресурсов. Первый вариант - использовать безопасный дескриптор для упаковки неуправляемого ресурса. Второй вариант - определение финализатора. В этом случае рекомендуется использовать безопасную ручку.
Думаю, этой ссылкеэта ссылка - самый ясный ответ на этот вопрос. Я не знаю, почему люди дают сложные объяснения на этот вопрос в Интернете. Я смутился, пока не нашел эту ссылку.
Как мы знаем, dispose и finalize используются для освобождения неуправляемых ресурсов... но разница заключается в том, что finalize использует два цикла для освобождения ресурсов, где для dispose используется один цикл...
Чтобы ответить по первой части, вы должны привести примеры, когда люди используют разные подходы для одного и того же класса-объекта. Иначе сложно (или даже странно) ответить.
Что касается второго вопроса, лучше сначала прочтите это Правильное использование интерфейса IDisposable, в котором утверждается, что
Это твой выбор! Но выберите Dispose.
Другими словами: GC знает только о финализаторе (если есть. Также известный как деструктор в Microsoft). Хороший код попытается очистить оба (финализатор и Dispose).