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

Импортируйте lodash как этот

import * as _ from "lodash"; вместо import _ from 'lodash';

27
задан Cœur 15 April 2017 в 17:16
поделиться

5 ответов

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

24
ответ дан Jon Skeet 28 November 2019 в 05:34
поделиться

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

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

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

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

5
ответ дан Oded 28 November 2019 в 05:34
поделиться

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

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

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

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

3
ответ дан ABCD 28 November 2019 в 05:34
поделиться

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

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

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

1
ответ дан Dave Black 28 November 2019 в 05:34
поделиться

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

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

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

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

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

3
ответ дан 28 November 2019 в 05:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: