Я видел несколько вопросов о финализаторах и IDisposable, у stackoverflow также должно быть что-то о GC.SupressFinalize и слабых ссылках
Сэм Саффрон,
Я не думаю, что слабые ссылки что-то значат в отношении финализаторов - возможно, вам следует опубликовать более прямой вопрос о них.
Майкл Берр
Да, я собирался опубликовать отдельный вопрос о слабых ссылках, все это может быть связано при создании пулов объектов. Также я должен задать вопрос о возрождении объекта аля ReRegisterForFinalize
Сэм Саффрон
Ответы:
297
SuppressFinalizeдолжен вызываться только классом, имеющим финализатор. Он сообщает сборщику мусора (GC), что thisобъект был полностью очищен.
Рекомендуемый IDisposableшаблон, когда у вас есть финализатор:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(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;}}publicvoidDispose()// Implement IDisposable{Dispose(true);
GC.SuppressFinalize(this);}~MyClass()// the finalizer{Dispose(false);}}
Обычно CLR хранит вкладки на объектах с помощью финализатора, когда они создаются (что делает их более дорогими при создании). SuppressFinalizeсообщает GC, что объект был очищен должным образом и не должен идти в очередь финализатора. Это похоже на деструктор C ++, но не действует ни на что подобное.
SuppressFinalizeОптимизация не является тривиальной, так как ваши объекты могут жить долгое время ожидания в очереди финализатора. Не поддавайтесь искушению обращаться SuppressFinalizeк другим объектам. Это серьезный дефект, который должен произойти.
Рекомендации по проектированию сообщают нам, что финализатор не требуется, если ваш объект реализует IDisposable, но если у вас есть финализатор, вы должны реализовать его, IDisposableчтобы разрешить детерминированную очистку вашего класса.
Большую часть времени вы должны уметь IDisposableубирать ресурсы. Финализатор вам нужен только тогда, когда ваш объект удерживает неуправляемые ресурсы, и вы должны гарантировать, что эти ресурсы очищены.
Примечание. Иногда кодеры добавляют финализатор для отладки сборок своих IDisposableклассов для проверки того, что код IDisposableправильно расположил свой объект.
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
В первом фрагменте кода я просто публикую информацию о том, как выглядит рекомендуемый шаблон IDisposable + финализатор. Отладка кода это хорошо, но это может отвлекать. .. Я могу только рекомендовать избегать финализаторов, за исключением классов, которые имеют неуправляемые ресурсы. Написание безопасного кода финализатора нетривиально.
Роберт Полсон
1
Привет, Почему нам нужно вызвать dispose с false в качестве параметра из финализатора? Что делать, если распоряжение никогда не вызывали, а затем оно не будет распоряжаться? Что если мы просто проверим, был ли удален объект или нет, и проведем реальную очистку.
Мечтатель
3
@Dreamer - это зависит от вашей реализации. В общем, вы хотите знать, вызывается ли Dispose финализатором по сравнению с реализацией IDisposable.Dispose (). Если вызывается из финализатора, вы должны предположить, что личные ссылки больше не действительны, и вы действительно ничего не можете сделать. Однако, если вызывается из IDisposable.Dispose (), вы знаете, что ссылки по-прежнему действительны.
Роберт Полсон
32
Если реализации класса IDisposableнет sealed, тогда он должен включать вызов, GC.SuppressFinalize(this)даже если он не включает определяемый пользователем финализатор . Это необходимо для обеспечения правильной семантики для производных типов, которые добавляют определяемый пользователем финализатор, но только переопределяют защищенный Dispose(bool)метод.
Сэм Харуэлл
1
sealedВажно не быть упомянутым @SamHarwell для производных классов. CodeAnalysis приводит к ca1816 + ca1063, когда класс не запечатан, но запечатанные классы хороши без него SuppressFinalize.
Дашесы
38
SupressFinalizeсообщает системе, что любая работа, выполненная в финализаторе, уже выполнена, поэтому финализатор вызывать не нужно. Из документов .NET:
Объекты, которые реализуют интерфейс IDisposable, могут вызывать этот метод из метода IDisposable.Dispose, чтобы сборщик мусора не вызывал Object.Finalize для объекта, который ему не требуется.
В общем, любой Dispose()метод должен вызывать большинство GC.SupressFinalize(), потому что он должен очищать все, что будет очищено в финализаторе.
SupressFinalizeэто просто то, что обеспечивает оптимизацию, которая позволяет системе не беспокоить очередь объекта в поток финализатора. Правильно написанный Dispose()/ финализатор должен работать правильно с вызовом или без него GC.SupressFinalize().
Этот метод должен вызываться для Disposeметода объектов, который реализует IDisposable, таким образом, GC не будет вызывать финализатор в другой раз, если кто-то вызовет Disposeметод.
Я думаю, что «Must» - это неправильно, даже не «должен» - просто в некоторых сценариях вы можете исключить накладные расходы на постановку в очередь / завершение объекта.
Базовый
1
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()может не всегда быть необходимым, но это не будет быть неправым.
Ответы:
SuppressFinalize
должен вызываться только классом, имеющим финализатор. Он сообщает сборщику мусора (GC), чтоthis
объект был полностью очищен.Рекомендуемый
IDisposable
шаблон, когда у вас есть финализатор:Обычно CLR хранит вкладки на объектах с помощью финализатора, когда они создаются (что делает их более дорогими при создании).
SuppressFinalize
сообщает GC, что объект был очищен должным образом и не должен идти в очередь финализатора. Это похоже на деструктор C ++, но не действует ни на что подобное.SuppressFinalize
Оптимизация не является тривиальной, так как ваши объекты могут жить долгое время ожидания в очереди финализатора. Не поддавайтесь искушению обращатьсяSuppressFinalize
к другим объектам. Это серьезный дефект, который должен произойти.Рекомендации по проектированию сообщают нам, что финализатор не требуется, если ваш объект реализует
IDisposable
, но если у вас есть финализатор, вы должны реализовать его,IDisposable
чтобы разрешить детерминированную очистку вашего класса.Большую часть времени вы должны уметь
IDisposable
убирать ресурсы. Финализатор вам нужен только тогда, когда ваш объект удерживает неуправляемые ресурсы, и вы должны гарантировать, что эти ресурсы очищены.Примечание. Иногда кодеры добавляют финализатор для отладки сборок своих
IDisposable
классов для проверки того, что кодIDisposable
правильно расположил свой объект.источник
IDisposable
нетsealed
, тогда он должен включать вызов,GC.SuppressFinalize(this)
даже если он не включает определяемый пользователем финализатор . Это необходимо для обеспечения правильной семантики для производных типов, которые добавляют определяемый пользователем финализатор, но только переопределяют защищенныйDispose(bool)
метод.sealed
Важно не быть упомянутым @SamHarwell для производных классов. CodeAnalysis приводит к ca1816 + ca1063, когда класс не запечатан, но запечатанные классы хороши без негоSuppressFinalize
.SupressFinalize
сообщает системе, что любая работа, выполненная в финализаторе, уже выполнена, поэтому финализатор вызывать не нужно. Из документов .NET:В общем, любой
Dispose()
метод должен вызывать большинствоGC.SupressFinalize()
, потому что он должен очищать все, что будет очищено в финализаторе.SupressFinalize
это просто то, что обеспечивает оптимизацию, которая позволяет системе не беспокоить очередь объекта в поток финализатора. Правильно написанныйDispose()
/ финализатор должен работать правильно с вызовом или без негоGC.SupressFinalize()
.источник
Этот метод должен вызываться для
Dispose
метода объектов, который реализуетIDisposable
, таким образом, GC не будет вызывать финализатор в другой раз, если кто-то вызоветDispose
метод.См .: метод GC.SuppressFinalize (Object) - Документы Microsoft
источник
Если объект имеет финализатор, .net помещает ссылку в очередь финализации.
Поскольку у нас есть вызов
Dispose(ture)
, он очищает объект, поэтому нам не нужна очередь завершения для выполнения этой работы.Поэтому вызовите
GC.SuppressFinalize(this)
удаление ссылки в очереди завершения.источник
Если класс или что-либо производное от него может содержать последнюю живую ссылку на объект с финализатором, то любой
GC.SuppressFinalize(this)
илиGC.KeepAlive(this)
должен быть вызван для объекта после любой операции, на которую этот финализатор может отрицательно повлиять, гарантируя, что финализатор выиграл не запускайте до тех пор, пока эта операция не будет завершена.Стоимость
GC.KeepAlive()
иGC.SuppressFinalize(this)
, по сути, одинакова в любом классе, в котором нет финализатора, и классы, у которых действительно есть финализаторы, обычно должны вызыватьсяGC.SuppressFinalize(this)
, поэтому использование последней функции в качестве последнего шагаDispose()
может не всегда быть необходимым, но это не будет быть неправым.источник