Почему мы должны вызывать SuppressFinalize, когда у нас нет деструктора - PullRequest
24 голосов
/ 09 апреля 2010

У меня есть несколько Вопросов, на которые я не могу получить правильный ответ.

1) Почему мы должны вызывать SuppressFinalize в функции Dispose, когда у нас нет деструктора.

2) Утилизация и финализация используются для освобождения ресурсов перед сборкой мусора. Независимо от того, является ли это управляемым или неуправляемым ресурсом, нам нужно его освободить, тогда зачем нам нужно условие внутри функции dispose, говоря «pass», когда мы вызываем эту переопределенную функцию из IDisposable: Dispose и передаем false при вызове из finalize.

См. Код ниже, который я скопировал из сети.

class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose(false);
     }

     protected void Dispose(bool disposing)
     {
       if (disposing)
       {
         // Code to dispose the managed resources of the class
       }
       // Code to dispose the un-managed resources of the class

       isDisposed = true;
     }

     public void Dispose()
     {
       Dispose(true);
       GC.SuppressFinalize(this);
     }
   }

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

   class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose();
     }


     public void Dispose()
     {
      // Code to dispose the managed resources of the class
      // Code to dispose the un-managed resources of the class
      isDisposed = true;

      // Call this since we have a destructor . what if , if we don't have one 
       GC.SuppressFinalize(this);
     }
   }       

Ответы [ 5 ]

22 голосов
/ 09 апреля 2010

Я выхожу на конечность, но ... большинству людей не не нужен полноценный образец избавления. Он спроектирован так, чтобы иметь прямой доступ к неуправляемым ресурсам (обычно через IntPtr) и к наследованию. В большинстве случаев ни один из них на самом деле не требуется.

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

public sealed class Foo : IDisposable
{
    private bool disposed;
    private FileStream stream;

    // Other code

    public void Dispose()
    {
        if (disposed)
        {
            return;
        }
        stream.Dispose();
        disposed = true;
    }
}

Обратите внимание, что этот не поточно-ориентированный, но это, вероятно, не будет проблемой.

Не нужно беспокоиться о возможности подклассов, напрямую удерживающих ресурсы, вам не нужно подавлять финализатор (потому что его нет) - и вам не нужно предоставлять способ подкласса настраивать удаление или. Жизнь проще без наследства.

Если вам нужно разрешить неконтролируемое наследование (т. Е. Вы не готовы поспорить, что у подклассов будут очень специфические потребности), тогда вам нужно перейти на полную схему.

Обратите внимание, что в SafeHandle из .NET 2.0 еще реже требуется собственный финализатор, чем в .NET 1.1.


Чтобы ответить на вопрос о том, почему в первую очередь существует флаг disposing: если вы работаете в финализаторе, другие объекты, на которые вы ссылаетесь, возможно, уже были завершены. Вы должны позволить им очистить себя, и вы должны очищать только ресурсы, которые непосредственно принадлежат.

5 голосов
/ 09 апреля 2010

Сохраните первую версию, она безопаснее и является правильной реализацией шаблона удаления.

  1. Вызов SuppressFinalize сообщает ГК, что вы сделали все уничтожение / утилизацию себя (ресурсов, находящихся в вашем классе) и что вам не нужно вызывать деструктор.

  2. Вам нужен тест на тот случай, если код, использующий ваш класс , уже уже вызвал dispose, и вы не должны указывать GC на удаление снова.

См. этот документ MSDN (методы Dispose должны вызывать SuppressFinalize).

3 голосов
/ 12 февраля 2013

1. Ответ на первый вопрос

По сути, вам не нужно вызывать метод SuppressFinalize, если в вашем классе нет метода finalize (Destructor). Я полагаю, что люди вызывают SupressFinalize, даже когда нет метода финализации из-за недостатка знаний.

2. Ответ на второй вопрос

Цель метода Finalize - освободить неуправляемые ресурсы. Самая важная вещь для понимания - это то, что метод Finalize вызывается, когда объект находится в очереди завершения. Сборщик мусора собирает все объекты, которые можно уничтожить. Сборщик мусора добавляет объекты, прошедшие финализацию, в очередь на финализацию перед уничтожением. Существует еще один фоновый процесс .net, который вызывает метод finalize для объектов, находящихся в очереди завершения. К тому времени, когда фоновый процесс выполнит метод finalize, другая управляемая ссылка этого конкретного объекта может быть уничтожена. Потому что нет конкретного порядка, когда дело доходит до выполнения финализации. Итак, Шаблон Dispose хочет убедиться, что метод finalize не пытается получить доступ к управляемым объектам. Вот почему управляемые объекты переходят в предложение «if (утилизация)», которое недоступно для метода finalize.

3 голосов
/ 29 апреля 2010

Вот основные факты

1) Object.Finalize - это то, что ваш класс переопределяет, когда у него есть финализатор. метод деструктора ~ TypeName () - это просто сокращение для 'override Finalize ()' и т. д.

2) Вы вызываете GC.SuppressFinalize, если вы утилизируете ресурсы в вашем методе Dispose перед финализацией (т.е. при выходе из блока использования и т. Д.). Если у вас нет финализатора, вам не нужно этого делать. Если у вас есть финализатор, это гарантирует, что объект будет удален из очереди финализации (поэтому мы не удаляем вещи дважды, так как финализатор обычно также вызывает метод Dispose)

3) Вы реализуете Финализатор как «отказоустойчивый» механизм. Финализаторы гарантированно будут работать (до тех пор, пока CLR не будет прервана), поэтому они позволяют вам удостовериться, что код очищен в случае, если метод Dispose не был вызван (возможно, программист забыл создать экземпляр в «использовании»). блок и т. д.

4) Финализаторы стоят дорого, поскольку Типы, которые имеют финализаторы, не могут быть собраны мусором в коллекции Поколения-0 (наиболее эффективной), и повышены до Поколения-1 со ссылкой на них в очереди F-Reachable, так что они представляют корень GC. только когда GC выполняет коллекцию Generation-1, вызывается финализатор, и ресурсы высвобождаются - поэтому реализуйте финализаторы только тогда, когда это очень важно - и убедитесь, что объекты, требующие финализации, настолько малы, насколько это возможно - потому что все объекты, которые могут будет достигнут вашим финализируемым объектом и будет повышен до Поколения-1.

1 голос
/ 22 сентября 2016

Вы всегда должны вызывать SuppressFinalize (), потому что вы можете иметь (или иметь в будущем) производный класс, который реализует Finalizer - в этом случае он вам нужен.

Допустим, у вас есть базовый класс, в котором нет финализатора - и вы решили не вызывать SuppressFinalize (). Затем через 3 месяца вы добавляете производный класс, который добавляет финализатор. Вполне вероятно, что вы забудете перейти к базовому классу и добавить вызов SuppressFinalize (). Нет никакого вреда в вызове, если нет финализатора.

Мой предложенный шаблон IDisposable размещен здесь: Как правильно реализовать шаблон Dispose

...