В .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()может не всегда быть необходимым, но это не будет быть неправым.