Завершить против утилизации - PullRequest
197 голосов
/ 09 апреля 2009

Почему некоторые люди используют метод Finalize вместо метода Dispose?

В каких ситуациях вы бы использовали метод Finalize вместо метода Dispose и наоборот?

Ответы [ 14 ]

127 голосов
/ 09 апреля 2009

Метод финализатора вызывается, когда ваш объект собирается сборщиком мусора, и у вас нет гарантии, когда это произойдет (вы можете форсировать его, но это снизит производительность).

С другой стороны, метод Dispose должен вызываться кодом, который создал ваш класс, чтобы вы могли очистить и освободить любые полученные вами ресурсы (неуправляемые данные, соединения с базой данных, файловые дескрипторы и т. Д.) момент, когда код сделан с вашим объектом.

Стандартная практика заключается в реализации IDisposable и Dispose, чтобы вы могли использовать свой объект в using оценке. Такие как using(var foo = new MyObject()) { }. И в вашем финализаторе вы звоните Dispose, на случай, если вызывающий код забыл вас утилизировать.

109 голосов
/ 09 апреля 2009

Другие уже рассмотрели разницу между Dispose и Finalize (кстати, метод Finalize все еще называется деструктором в спецификации языка), поэтому я просто добавлю немного о сценариях, где Finalize метод пригодится.

Некоторые типы инкапсулируют одноразовые ресурсы таким образом, чтобы их было легко использовать и избавляться от них одним действием. Общее использование часто так: открыть, прочитать или написать, закрыть (удалить). Он очень хорошо вписывается в конструкцию using.

Другие немного сложнее. WaitEventHandles для экземпляров не используются, как это, поскольку они используются для передачи сигналов из одного потока в другой. Тогда возникает вопрос, кто должен назвать Dispose по этим вопросам? В качестве защитных типов, подобных этим, реализован метод Finalize, который обеспечивает удаление ресурсов, когда приложение больше не ссылается на экземпляр.

63 голосов
/ 09 апреля 2009

Finalize - это метод backstop, вызываемый сборщиком мусора, когда он восстанавливает объект. Dispose - это метод «детерминированной очистки», который вызывается приложениями для освобождения ценных собственных ресурсов (дескрипторов окон, соединений с базами данных и т. Д.), Когда они больше не нужны, вместо того, чтобы оставлять их удерживаемыми на неопределенный срок до тех пор, пока GC не получит объект. 1001 *

Как пользователь объекта, вы всегда используете Dispose. Финализация для GC.

Как разработчик класса, если вы располагаете управляемыми ресурсами, которые должны быть утилизированы, вы реализуете Dispose. Если у вас есть собственные ресурсы, вы реализуете и Dispose и Finalize, и оба вызываете общий метод, который освобождает собственные ресурсы. Эти идиомы обычно объединяются с помощью закрытого метода Dispose (bool dispose), который Dispose вызывает со значением true, а Finalize - со значением false. Этот метод всегда освобождает собственные ресурсы, затем проверяет параметр распоряжения, и если он истинен, он удаляет управляемые ресурсы и вызывает GC.SuppressFinalize.

40 голосов
/ 10 ноября 2009

Доработка

  • Финализаторы всегда должны быть 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.
28 голосов
/ 09 апреля 2009

Finalize вызывается GC, когда этот объект больше не используется.

Dispose - это обычный метод, который пользователь этого класса может вызвать для освобождения любых ресурсов.

Если пользователь забыл вызвать Dispose и если в классе реализован Finalize, то GC удостоверится, что он вызван.

16 голосов
/ 07 сентября 2014

Некоторые ключи из книги 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);
        }
    }
}
7 голосов
/ 09 апреля 2009

99% времени, вам не нужно беспокоиться ни о чем. :) Но, если ваши объекты содержат ссылки на неуправляемые ресурсы (например, дескрипторы окон, дескрипторы файлов), вам необходимо предоставить способ, которым управляемый объект освобождает эти ресурсы. Finalize дает неявный контроль над высвобождением ресурсов. Вызывается сборщиком мусора. Dispose - это способ явно контролировать освобождение ресурсов и может вызываться напрямую.

Существует гораздо больше, чтобы узнать о предмете Сборка мусора , но это только начало.

5 голосов
/ 28 ноября 2011

Финализатор предназначен для неявной очистки - вы должны использовать это всякий раз, когда класс управляет ресурсами, которые должны быть очищены, так как в противном случае вы бы пропустили дескрипторы / память и т. Д. *

Корректная реализация финализатора общеизвестно трудна, и ее следует избегать везде, где это возможно - класс SafeHandle (доступен в .Net v2.0 и более поздних версиях) теперь означает, что вам очень редко (если когда-либо) нужно реализовывать финализатор больше .

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

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

См. Обновление DG: удаление, финализация и управление ресурсами , что я считаю лучшим и наиболее полным набором рекомендаций для финализаторов и IDisposable.

2 голосов
/ 17 августа 2018

Резюме -

  • Вы пишете финализатор для своего класса, если он имеет ссылку на неуправляемый ресурсы, и вы хотите убедиться, что эти неуправляемые ресурсы освобождаются, когда экземпляр этого класса собирается мусором автоматически . Обратите внимание, что вы не можете вызывать 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);
     }
1 голос
/ 01 июня 2017

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

Обратите внимание, что даже когда вы предоставляете явное управление с помощью Dispose, вы должны обеспечивать неявную очистку с помощью метода Finalize. Finalize обеспечивает резервное копирование для предотвращения постоянной утечки ресурсов, если программист не может вызвать Dispose.

...