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