У .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()
може не завжди бути необхідним, але це не буде помилятися.