Почему мы должны называть SuppressFinalize, когда у нас нет деструктора

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

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

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

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

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);
     }
   }       

Ответы

Ответ 1

Я выхожу на конечность здесь, но... большинству людей не нужна полномасштабная модель размещения. Он предназначен для обеспечения надежного доступа к неуправляемым ресурсам (обычно через 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 в первую очередь: если вы работаете в финализаторе, другие объекты, на которые вы ссылаетесь, возможно, уже завершены. Вы должны позволить им очистить себя, и вы должны только очистить ресурсы, которыми вы владеете непосредственно.

Ответ 2

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

  • Вызов SuppressFinalize сообщает GC, что вы сделали все уничтожение/распоряжение собой (ресурсов, принадлежащих вашему классу), и что ему не нужно вызывать деструктор.

  • Тебе нужен тест, если код, используемый вашим классом, уже вызывается dispose, и вы не должны указывать GC для утилизации снова.

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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