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

У меня вопрос, как использовать 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 (это) в Dispose ()

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

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

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

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

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

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

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


person Daniel Peñalba    schedule 06.01.2011    source источник
comment
AFAIK этот шаблон устарел. Для неуправляемых ресурсов обычно следует использовать SafeHandle, а для управляемых ресурсов обычно не требуется финализатор.   -  person CodesInChaos    schedule 06.01.2011
comment
@codein, вы правы, но есть ли где-нибудь каноническая рецензия на MSDN?   -  person Henk Holterman    schedule 06.01.2011
comment
@CodeInChaos: Не могли бы вы предоставить немного литературы по этому поводу?   -  person Daniel Peñalba    schedule 06.01.2011
comment
Лично я подписываюсь на точку зрения, согласно которой единственное, что должен делать (некритичный) финализатор, - это уведомлять программиста о потенциальной ошибке.   -  person CodesInChaos    schedule 06.01.2011
comment
У меня нет под рукой канонических ссылок, но есть несколько блогов, в которых описываются либо проблемы с нормальной финализацией (в частности, она не всегда выполняется и ее трудно кодировать), либо преимущества безопасных дескрипторов / критической финализации blogs.msdn.com/b/bclteam/archive/2005/03/15/396335 .aspx blogs.msdn.com/ b / cbrumme / archive / 2004/02/20 / 77460.aspx msdn.microsoft.com/en-us/library/fh21e17c (v = vs.90) .aspx   -  person CodesInChaos    schedule 06.01.2011
comment
@HenkHolterman Его нет в MSDN, но он написан Стивеном Клири IDisposable: что ваша мама никогда не говорила вам о распределении ресурсов   -  person Scott Chamberlain    schedule 27.08.2016


Ответы (3)


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

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

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

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

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

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

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

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

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

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

person Henk Holterman    schedule 06.01.2011
comment
@Henk: После вашего редактирования о 2a: Что означает, что неуправляемые ресурсы всегда очищаются, даже если программист забыл вызвать dispose? - person Daniel Peñalba; 06.01.2011
comment
-1 Неверно. Вы можете не удалять ресурсы в финализаторе. Ссылки могут быть недействительными, поэтому их нельзя трогать. - person Guffa; 06.01.2011
comment
@Guffa: Ссылка не может стать недействительной, пока вы ее держите. Типовая безопасность сохраняется вплоть до Finalize. Это просто сложно: myField уже мог быть удален (но не собран!). Dispose () для объекта Disposed должен быть безопасным. - person Henk Holterman; 06.01.2011
comment
@Guffa: Я думаю, что Хенк прав, потому что он имеет в виду избавиться от управляемых объектов внутри блока disposing, и это не финализатор, это вызов Dispose(), сделанный программистом. - person Daniel Peñalba; 06.01.2011
comment
Что означает, что неуправляемые ресурсы всегда чистятся, даже если программист забыл вызвать dispose? Нет, только если финализатор запускается. Есть несколько сценариев, в которых он не работает. В частности при принудительной выгрузке app-домена. Это вызывает проблемы в приложениях с несколькими доменами приложений. - person CodesInChaos; 06.01.2011
comment
Хенк прав, но ... стоит еще раз подчеркнуть, что, ссылаясь на управляемый IDisposable ресурс в вашем финализаторе, вы можете искусственно поддерживать его жизнь дольше, чем необходимо. Если вы не указали его в финализаторе, тогда GC будет свободен забрать его (и вызвать свой резервный финализатор, если он у него есть), как только он больше не понадобится. Нет необходимости избавляться от управляемых IDisposable ресурсов в вашем финализаторе: если эти ресурсы нуждаются в какой-либо резервной доработке, они должны обработать это самостоятельно внутри. - person LukeH; 06.01.2011
comment
@Daniel, ... всегда убирал, даже программист забыл call dispose? Обычно да, за некоторыми (редкими) исключениями, отмеченными CodeInChaos. Но «забвение» Dispose () всегда (очень) неэффективно и может привести к сбою. - person Henk Holterman; 06.01.2011
comment
@ Даниэль Пеньяльба: Если он имеет в виду это, он определенно выразился очень плохо, поскольку он отвечает на вопрос об избавлении от финализатора. Я не вижу смысла объяснять, что вы можете сделать при утилизации, в качестве ответа на то, что вы можете сделать после завершения. - person Guffa; 06.01.2011
comment
@Guffa: Я отвечал об операторе if(), который вызывается как в Finalizer , так и Dispose () ... Что / когда было сутью вопроса, но, возможно, я прочитал так же плохо, как Я выражаюсь. - person Henk Holterman; 06.01.2011
comment
@ Хенк Холтерман: Если бы вы были такими, вы бы действительно плохо выражали себя. Очевидно, вы отвечали на вопрос, почему мы не можем освободить ресурсы с помощью деструктора ?, поскольку ответ следует за заголовком и принимает форму прямого ответа на него. - person Guffa; 06.01.2011
comment
@Guffa, правильно, вы должны вернуться к вопросу, чтобы знать, что конкретно (на данный момент) касается управляемых ресурсов. - person Henk Holterman; 06.01.2011
comment
@ Хенк Холтерман: Вы полностью упускаете суть. Что можно сделать в финализаторе - это еще вопрос. - person Guffa; 06.01.2011
comment
@Guffa, Да, и я ответил, что вы можете, но не должны вызывать Dispose () для принадлежащего объекта. Похоже, мы не согласны по поводу банки. - person Henk Holterman; 06.01.2011

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

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

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

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

person NOtherDev    schedule 06.01.2011
comment
К вашему сведению, после того, как система определит, что некоторые объекты имеют право на финализацию, все такие объекты и все объекты, на которые они имеют прямые или косвенные ссылки, станут активными до тех пор, пока финализаторы не запустятся. Если финализируемый объект X содержит единственную существующую ссылку на финализируемый объект Y, и ничто не содержит ссылку на X, тогда, когда X.Finalize () запускается, объект Y может иметь или не иметь свою подпрограмму Finalize (), но объект Y является гарантированно будет существовать. - person supercat; 26.10.2011

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

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

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

person supercat    schedule 19.11.2015