Два вопроса об Dispose() и деструкторах в С#

У меня вопрос о том, как использовать Dispose() и деструкторы. Читая несколько статей и документацию MSDN , это, по-видимому, является рекомендуемым способом реализации Dispose() и деструкторов.

Но у меня есть два вопроса об этой реализации, которые вы можете прочитать ниже:

class Testing : IDisposable
{
    bool _disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed) // only dispose once!
        {
            if (disposing)
            {
                // Not in destructor, OK to reference other objects
            }
            // perform cleanup for this object
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);

        // tell the GC not to finalize
        GC.SuppressFinalize(this);
    }

    ~Testing()
    {
        Dispose(false);
    }
}

GC.SupressFinalize(this) на Dispose()

Когда программист использует using или вызывает функцию Dispose(), наш класс вызывает GC.SupressFinalize(this). Мой вопрос здесь:

  • Что это значит? Будет ли собираться объект, но без вызова деструктора?. Я полагаю, что ответ на вопрос "да", поскольку деструкторы преобразуются каркасом в вызов Finalize(), но я не уверен.

Завершение без вызова Dispose()

Предположим, что GC собирается очистить наш объект, но программист не вызвал Dispose()

  • Почему мы не располагаем ресурсами на этом этапе? Другими словами, почему мы не можем освобождать ресурсы от деструктора?
  • Какой код должен быть выполнен в if, внутри и вне?

    if (!_disposed) // only dispose once!
    {
       if (disposing)
       {
           //What should I do here and why?
       }
       // And what here and why?
    }
    

Заранее спасибо

Ответы

Ответ 1

1. Что делает SuppressFinalize?

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

2. Почему бы нам не распорядиться [управляемым] ресурсом на данный момент? Другими словами, почему мы не можем освобождать [управляемые] ресурсы на деструкторе?

Вы могли бы, но это, несомненно, будет бессмысленным: объект, в котором вы находитесь, стал недоступным, поэтому все принадлежащие управляемым ресурсам также недоступны. Они будут завершены и собраны GC в том же запуске, а вызов Dispose() на них не нужен, но не полностью без риска или стоимости.

2a Какой код должен быть выполнен в if, внутри и вне?

Внутри if(disposing) вызовите _myField.Dispose()

Другими словами, Dispose из управляемых ресурсов (объектов с Dispose)

Снаружи код вызова для очистки (закрытия) неуправляемых ресурсов, например Win32API.Close(_myHandle).

Обратите внимание, что когда у вас нет неуправляемых ресурсов, как это обычно бывает (посмотрите SafeHandle), вам не нужен деструктор и, следовательно, нет SuppressFinalize.

И это означает, что полная (официальная) реализация этого шаблона необходима только из-за возможности унаследовать Test. Обратите внимание, что Dispose(bool) защищен. Когда вы объявляете, что ваше тестирование класса sealed, оно абсолютно безопасно и соответствует опусканию ~Testing().

Ответ 2

Первая часть:

Когда вызывается GC.SupressFinalize(this), GC сообщается, что объект уже освободил свои ресурсы, и он может быть собран в мусор, как и любой другой объект. И да, финализация и "деструкторы" - одно и то же в .NET.

Вторая часть:

Завершение выполняется отдельным потоком, и мы не контролируем время и порядок завершения, поэтому мы не знаем, доступны ли еще какие-либо объекты или уже завершены. По этой причине вы не можете ссылаться на другой объект вне блока disposing.

Ответ 3

Подавляющее большинство времени, когда объект, которому принадлежат ресурсы IDisposable, завершен, по крайней мере одно из следующих утверждений будет применяться к каждому из этих ресурсов:

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

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