У .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.Розпоряджуйте метод, щоб запобігти виклику сміттєзбірника Object.Finalize на об'єкт, який цього не вимагає.
Загалом, більшість будь-яких Dispose()методів повинні бути спроможні викликати GC.SupressFinalize(), оскільки він повинен очистити все, що було б очищено у фіналізаторі.
SupressFinalizeце лише щось, що забезпечує оптимізацію, яка дозволяє системі не турбувати чергування об’єкта до потоку фіналізатора. Правильно написаний Dispose()/ фіналізатор повинен працювати належним чином із закликом або без нього GC.SupressFinalize().
Цей метод повинен бути викликаний Disposeметодом об'єктів, що реалізує IDisposable, таким чином, GC не викликає фіналізатор в інший раз, якщо хтось викликає Disposeметод.
Дивіться: Метод GC.SuppressFinalize (Object) - Microsoft Docs
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()може не завжди бути необхідним, але це не буде помилятися.