В .NET, при каких обстоятельствах я должен использовать GC.SuppressFinalize()
?
Какие преимущества дает мне этот метод?
В .NET, при каких обстоятельствах я должен использовать GC.SuppressFinalize()
?
Какие преимущества дает мне этот метод?
Ответы:
SuppressFinalize
должен вызываться только классом, имеющим финализатор. Он сообщает сборщику мусора (GC), что this
объект был полностью очищен.
Рекомендуемый IDisposable
шаблон, когда у вас есть финализатор:
public class MyClass : IDisposable
{
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// called via myClass.Dispose().
// OK to use any private object references
}
// Release unmanaged resources.
// Set large fields to null.
disposed = true;
}
}
public void Dispose() // Implement IDisposable
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass() // the finalizer
{
Dispose(false);
}
}
Обычно CLR хранит вкладки на объектах с помощью финализатора, когда они создаются (что делает их более дорогими при создании). SuppressFinalize
сообщает GC, что объект был очищен должным образом и не должен идти в очередь финализатора. Это похоже на деструктор C ++, но не действует ни на что подобное.
SuppressFinalize
Оптимизация не является тривиальной, так как ваши объекты могут жить долгое время ожидания в очереди финализатора. Не поддавайтесь искушению обращаться SuppressFinalize
к другим объектам. Это серьезный дефект, который должен произойти.
Рекомендации по проектированию сообщают нам, что финализатор не требуется, если ваш объект реализует IDisposable
, но если у вас есть финализатор, вы должны реализовать его, IDisposable
чтобы разрешить детерминированную очистку вашего класса.
Большую часть времени вы должны уметь IDisposable
убирать ресурсы. Финализатор вам нужен только тогда, когда ваш объект удерживает неуправляемые ресурсы, и вы должны гарантировать, что эти ресурсы очищены.
Примечание. Иногда кодеры добавляют финализатор для отладки сборок своих IDisposable
классов для проверки того, что код IDisposable
правильно расположил свой объект.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
нет sealed
, тогда он должен включать вызов, GC.SuppressFinalize(this)
даже если он не включает определяемый пользователем финализатор . Это необходимо для обеспечения правильной семантики для производных типов, которые добавляют определяемый пользователем финализатор, но только переопределяют защищенный Dispose(bool)
метод.
sealed
Важно не быть упомянутым @SamHarwell для производных классов. CodeAnalysis приводит к ca1816 + ca1063, когда класс не запечатан, но запечатанные классы хороши без него SuppressFinalize
.
SupressFinalize
сообщает системе, что любая работа, выполненная в финализаторе, уже выполнена, поэтому финализатор вызывать не нужно. Из документов .NET:
Объекты, которые реализуют интерфейс IDisposable, могут вызывать этот метод из метода IDisposable.Dispose, чтобы сборщик мусора не вызывал Object.Finalize для объекта, который ему не требуется.
В общем, любой Dispose()
метод должен вызывать большинство GC.SupressFinalize()
, потому что он должен очищать все, что будет очищено в финализаторе.
SupressFinalize
это просто то, что обеспечивает оптимизацию, которая позволяет системе не беспокоить очередь объекта в поток финализатора. Правильно написанный Dispose()
/ финализатор должен работать правильно с вызовом или без него GC.SupressFinalize()
.
Этот метод должен вызываться для Dispose
метода объектов, который реализует IDisposable
, таким образом, GC не будет вызывать финализатор в другой раз, если кто-то вызовет Dispose
метод.
См .: метод GC.SuppressFinalize (Object) - Документы Microsoft
Dispose(true);
GC.SuppressFinalize(this);
Если объект имеет финализатор, .net помещает ссылку в очередь финализации.
Поскольку у нас есть вызов Dispose(ture)
, он очищает объект, поэтому нам не нужна очередь завершения для выполнения этой работы.
Поэтому вызовите GC.SuppressFinalize(this)
удаление ссылки в очереди завершения.
Если класс или что-либо производное от него может содержать последнюю живую ссылку на объект с финализатором, то любой GC.SuppressFinalize(this)
или GC.KeepAlive(this)
должен быть вызван для объекта после любой операции, на которую этот финализатор может отрицательно повлиять, гарантируя, что финализатор выиграл не запускайте до тех пор, пока эта операция не будет завершена.
Стоимость GC.KeepAlive()
и GC.SuppressFinalize(this)
, по сути, одинакова в любом классе, в котором нет финализатора, и классы, у которых действительно есть финализаторы, обычно должны вызываться GC.SuppressFinalize(this)
, поэтому использование последней функции в качестве последнего шага Dispose()
может не всегда быть необходимым, но это не будет быть неправым.