Ответы:
Другие уже рассмотрели разницу между Dispose
и Finalize
(кстати, Finalize
метод все еще называется деструктором в спецификации языка), поэтому я просто добавлю немного о сценариях, где Finalize
метод пригодится.
Некоторые типы инкапсулируют одноразовые ресурсы таким образом, чтобы их было легко использовать и избавляться от них одним действием. Общее использование часто так: открыть, прочитать или написать, закрыть (удалить). Это очень хорошо вписывается в using
конструкцию.
Другие немного сложнее. WaitEventHandles
поскольку экземпляры не используются, как это, поскольку они используются для передачи сигналов от одного потока к другому. Тогда возникает вопрос: кому это нужно Dispose
? В качестве защитных типов, подобных этим, реализуется Finalize
метод, который обеспечивает удаление ресурсов, когда приложение больше не ссылается на экземпляр.
Finalize
может быть оправдано, - это когда несколько объектов заинтересованы в том, чтобы ресурс оставался в живых, но нет средства, с помощью которого объект, который перестает интересоваться ресурсом, может узнать, является ли он последний. В таком случае Finalize
обычно срабатывает только когда никто не заинтересован в объекте. Потеря времени работы 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. Этот метод всегда освобождает собственные ресурсы, затем проверяет параметр распоряжения, и если он истинен, он удаляет управляемые ресурсы и вызывает GC.SuppressFinalize.
Dispose
это хорошо, и правильно его реализовать, как правило, легко. Finalize
это зло, и правильно его реализовать, как правило, сложно. Помимо прочего, поскольку сборщик мусора гарантирует, что идентификация объекта никогда не будет «переработана», пока существует какая-либо ссылка на этот объект, легко очистить группу Disposable
объектов, некоторые из которых, возможно, уже были очищены, без проблем; любая ссылка на объект, для которого Dispose
уже был вызван, останется ссылкой на объект, для которого Dispose
уже был вызван.
Fred
владеет файловым дескриптором № 42 и закрывает его, система может присоединить тот же номер к некоторому файловому дескриптору, который присвоен другому объекту. В этом случае дескриптор файла № 42 будет ссылаться не на закрытый файл Фреда, а на файл, который активно использовался этим другим объектом; для , Fred
чтобы попытаться закрыть ручку # 42 снова будет иметь катастрофические последствия . Попытка 100% надежного отслеживания того, был ли освобожден один неуправляемый объект, осуществима. Пытаться отслеживать несколько объектов гораздо сложнее.
Доработка
protected
, нет public
или нет, private
чтобы метод не мог быть вызван напрямую из кода приложения, и в то же время он может вызывать base.Finalize
методDispose
IDisposable
на каждом типе, который имеет финализаторDispose
метода. Другими словами, избегайте использования объекта после вызова Dispose
метода.Dispose
на все IDisposable
типы, как только вы закончите с нимиDispose
несколько раз, не вызывая ошибок.Dispose
метода, используя GC.SuppressFinalize
методDispose
методовУтилизировать / Завершить шаблон
Dispose
и Finalize
при работе с неуправляемыми ресурсами. Finalize
Реализация будет работать и ресурсы будут по- прежнему будут выпущены , когда объект мусора , даже если разработчик забыл вызвать Dispose
метод явно.Finalize
методе, так и в Dispose
методе. Дополнительно вызовите Dispose
метод для любых объектов .NET, которые есть у вас в качестве компонентов внутри этого класса (с неуправляемыми ресурсами в качестве их члена) из Dispose
метода.Finalize вызывается GC, когда этот объект больше не используется.
Dispose - это обычный метод, который пользователь этого класса может вызвать для освобождения любых ресурсов.
Если пользователь забыл вызвать Dispose и если в классе реализован Finalize, то GC позаботится о том, чтобы он был вызван.
Вот некоторые ключи из книги MCSD Certification Toolkit (экзамен 70-483), стр. 193:
деструктор ≈ (он почти равен)base.Finalize()
, деструктор преобразуется в переопределенную версию метода Finalize, который выполняет код деструктора и затем вызывает метод Finalize базового класса. Тогда это совершенно недетерминированный, вы не можете знать, когда будет вызван, потому что зависит от GC.
Если класс не содержит управляемых ресурсов и неуправляемых ресурсов , он не должен реализовывать IDisposable
или иметь деструктор.
Если класс имеет только управляемые ресурсы , он должен реализовать, IDisposable
но не должен иметь деструктора. (Когда деструктор выполняется, вы не можете быть уверены, что управляемые объекты все еще существуют, поэтому вы Dispose()
все равно не можете вызывать их методы.)
Если у класса есть только неуправляемые ресурсы , он должен быть реализован IDisposable
и нуждается в деструкторе, если программа не вызывает Dispose()
.
Dispose()
Метод должен быть безопасным для запуска более одного раза. Вы можете добиться этого, используя переменную, чтобы отслеживать, была ли она запущена ранее.
Dispose()
должен освободить как управляемые, так и неуправляемые ресурсы .
Деструктор должен освобождать только неуправляемые ресурсы . Когда деструктор выполняется, вы не можете быть уверены, что управляемые объекты все еще существуют, поэтому вы все равно не можете вызывать их методы Dispose. Это достигается путем использования канонического protected void Dispose(bool disposing)
шаблона, когда освобождаются (удаляются) только управляемые ресурсы disposing == true
.
После освобождения ресурсов 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);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// 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;
}
}
}
В 99% случаев вам не нужно беспокоиться об этом. :) Но, если ваши объекты содержат ссылки на неуправляемые ресурсы (например, дескрипторы окон, дескрипторы файлов), вам нужно предоставить способ, которым управляемый объект освобождает эти ресурсы. Finalize дает неявный контроль над высвобождением ресурсов. Вызывается сборщиком мусора. Dispose - это способ явно контролировать освобождение ресурсов и может быть вызван напрямую.
Намного больше узнать о предмете сбора мусора , но это только начало.
Финализатор предназначен для неявной очистки - вы должны использовать это всякий раз, когда класс управляет ресурсами, которые абсолютно необходимо очистить, так как в противном случае вы бы потеряли дескрипторы / память и т. Д ...
Корректная реализация финализатора общеизвестно трудна, и ее следует избегать везде, где это возможно - SafeHandle
класс (доступен в .Net v2.0 и более поздних версиях) теперь означает, что вам очень редко (если вообще когда-либо) нужно реализовывать финализатор больше.
IDisposable
Интерфейс для явной очистки и гораздо чаще используется - вы должны использовать это , чтобы позволить пользователям явно освободить или очистки ресурсов , когда они закончили с использованием объекта.
Обратите внимание, что если у вас есть финализатор, вам также следует реализовать IDisposable
интерфейс, позволяющий пользователям явно освобождать эти ресурсы раньше, чем они были бы, если бы объект собирался мусором.
См. Обновление DG: Распоряжение, Финализация и Управление ресурсами для того, что я считаю лучшим и наиболее полным набором рекомендаций по финализаторам и IDisposable
.
Резюме -
Кроме того, еще одно отличие состоит в том, что в реализации 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
}
}
Различаются методы Finalize и Dispose в C #.
GC вызывает метод finalize, чтобы вернуть неуправляемые ресурсы (такие как управление файлами, windows api, сетевое подключение, подключение к базе данных), но время не фиксируется, когда GC вызывает его. Он неявно вызывается GC, это означает, что у нас нет низкого уровня контроля над ним.
Метод Dispose: У нас есть низкий уровень контроля над ним, как мы его называем из кода. мы можем вернуть неуправляемые ресурсы всякий раз, когда считаем, что они непригодны для использования. Мы можем добиться этого путем внедрения шаблона IDisposal.
Экземпляры класса часто инкапсулируют контроль над ресурсами, которые не управляются средой выполнения, такими как дескрипторы окон (HWND), соединения с базой данных и так далее. Следовательно, вы должны предоставить как явный, так и неявный способ освобождения этих ресурсов. Обеспечьте неявное управление, реализовав защищенный метод Finalize для объекта (синтаксис деструктора в C # и управляемые расширения для C ++). Сборщик мусора вызывает этот метод в какой-то момент после того, как больше нет допустимых ссылок на объект. В некоторых случаях вы можете захотеть предоставить программистам, использующим объект, возможность явно освобождать эти внешние ресурсы до того, как сборщик мусора освободит объект. Если внешний ресурс является дефицитным или дорогим, можно добиться большей производительности, если программист явно освобождает ресурсы, когда они больше не используются. Чтобы обеспечить явное управление, реализуйте метод Dispose, предоставляемый интерфейсом IDisposable. Потребитель объекта должен вызывать этот метод, когда это делается с использованием объекта. Dispose может быть вызван, даже если другие ссылки на объект живы.
Обратите внимание, что даже когда вы предоставляете явный контроль посредством Dispose, вы должны обеспечивать неявную очистку с помощью метода Finalize. Finalize предоставляет резервную копию, чтобы предотвратить постоянную утечку ресурсов, если программист не может вызвать Dispose.
Основное различие между Dispose и Finalize заключается в том, что:
Dispose
обычно вызывается вашим кодом. Ресурсы освобождаются мгновенно, когда вы звоните. Люди забывают вызывать метод, поэтому using() {}
утверждение придумано. Когда ваша программа завершит выполнение кода внутри {}
, она автоматически вызовет Dispose
метод.
Finalize
не вызывается вашим кодом. Это значит быть вызванным сборщиком мусора (GC). Это означает, что ресурс может быть освобожден в любое время в будущем, когда GC решит это сделать. Когда GC выполняет свою работу, он проходит через множество методов Finalize. Если у вас есть тяжелая логика в этом, это замедлит процесс. Это может вызвать проблемы с производительностью вашей программы. Так что будьте осторожны с тем, что вы положили туда.
Я лично написал бы большую часть логики уничтожения в Dispose. Надеюсь, это прояснит путаницу.
Как мы знаем, dispose и finalize используются для освобождения неуправляемых ресурсов ... но разница заключается в том, что finalize использует два цикла для освобождения ресурсов, тогда как для dispose используется один цикл ...
Чтобы ответить на первую часть, вы должны привести примеры, когда люди используют разные подходы для одного и того же класса. Иначе трудно (или даже странно) ответить.
Что касается второго вопроса, то лучше сначала прочитать это Правильное использование интерфейса IDisposable, который утверждает, что
Это твой выбор! Но выберите Dispose.
Другими словами: сборщик мусора знает только о финализаторе (если таковой имеется. Также известен как деструктор для Microsoft). Хороший код попытается очистить от обоих (финализатор и удаление).