Відповіді:
Інші вже покрили різницю між Disposeі Finalize(btw Finalizeметод все ще називається деструктором у специфікації мови), тому я просто додам трохи про сценарії, де цей Finalizeметод стане у нагоді.
Деякі типи інкапсулюють одноразові ресурси таким чином, щоб їх було легко використовувати та розпоряджатись однією дією. Загальне використання часто таке: відкривати, читати чи писати, закривати (розпоряджатися). Він дуже добре вписується в usingконструкцію.
Іншим трохи складніше. WaitEventHandlesдля примірників не використовуються так, як вони використовуються для передачі сигналу від однієї нитки до іншої. Тоді стає питанням, хто повинен закликати Disposeїх? Як такі захисні типи, як ці реалізують Finalizeметод, який гарантує розміщення ресурсів, коли додаток більше не посилається на додаток.
Finalizeможе бути виправдано, це коли існує ряд об'єктів, які зацікавлені у збереженні ресурсу в живих, але немає способу, за допомогою якого об'єкт, який перестає цікавитись ресурсом, може з'ясувати, чи це останній. У такому випадку, Finalizeяк правило, стрілятимуть лише тоді, коли ніхто не зацікавиться об’єктом. Невичерпний термін роботи Finalizeжахливий для негорючих ресурсів, таких як файли та блокування, але може бути гаразд для змінних ресурсів.
Метод фіналізатора називається, коли ваш об’єкт збирається сміттям, і ви не маєте гарантії, коли це станеться (ви можете змусити його, але це зашкодить продуктивності).
DisposeМетод з іншого боку, призначений для виклику коду , який створив свій клас , так що ви можете очистити і звільнити ресурси , ви придбали (некеровані дані, з'єднання баз даних, файлові дескриптори, і т.д.) в той момент , код виконується з ваш об’єкт.
Стандартна практика полягає в тому, щоб реалізовувати IDisposableі Disposeтаким чином, щоб ви могли використовувати ваш об'єкт у складі using. Такі як using(var foo = new MyObject()) { }. А у вашому фіналізаторі ви зателефонуєте Dispose, на всякий випадок, якщо код, що дзвонить, забув розпоряджатися вами.
Фіналізація - це метод зворотного зупинки, який викликається сміттєзбірником, коли він відновлює об'єкт. Розпорядження - це метод "детермінованої очищення", який закликається програмами звільняти цінні внутрішні ресурси (ручки вікон, підключення до бази даних тощо), коли вони більше не потрібні, а не залишати їх утримувати на невизначений термін, поки GC не обійдеться до об'єкта.
Як користувач об'єкта, ви завжди використовуєте Dispose. Фіналізація призначена для GC.
Як реалізатор класу, якщо ви тримаєте керовані ресурси, які слід розпоряджатися, ви реалізуєте Dispose. Якщо ви володієте власними ресурсами, ви реалізуєте і розпоряджатись, і доопрацьовувати, і обидва викликаєте загальний метод, який вивільняє власні ресурси. Ці ідіоми, як правило, поєднуються за допомогою приватного методу Dispose (bool disposition), який розпоряджається викликами з істинним та фіналізує виклики з помилковими. Цей метод завжди звільняє власні ресурси, потім перевіряє параметр розпорядження, і якщо він правдивий, він розпоряджається керованими ресурсами та викликає GC.SuppressFinalize.
Disposeце добре, і правильно його реалізувати, як правило, просто. Finalizeце зло, і правильно його реалізувати, як правило, важко. Крім усього іншого, оскільки GC гарантуватиме, що особистість жодного об'єкта не буде "перероблена", поки існує будь-яка посилання на цей об'єкт, легко очистити купу Disposableоб'єктів, деякі з яких, можливо, вже були очищені, це нема проблем; будь-яке посилання на об'єкт, на який Disposeвже було викликано, залишатиметься посиланням на об'єкт, на який Disposeвже було викликано.
Fredналежить ручка файлу № 42 і закриває його, система може привласнити такий самий номер до деякої обробки файлів, яка надається якомусь іншому об'єкту. У такому випадку ручка файлу №42 стосуватиметься не закритого файлу Фреда, а файла, який активно використовувався іншим об'єктом; бо Fredспробувати закрити ручку №42 знову було б згубно. Намагаючись на 100% надійно відслідковувати, чи ще не керований об’єкт ще випущений, є працездатним. Намагатися відслідковувати кілька об’єктів набагато складніше.
Довершити
protected, ні, publicабо privateтак, щоб метод не можна було викликати безпосередньо з коду програми, і в той же час, він може робити виклик base.FinalizeметодуУтилізуйте
IDisposableдля кожного типу, який має фіналізаторDisposeметоду. Іншими словами, уникайте використання об'єкта після Disposeвиклику методу на ньому.Disposeдо всіх IDisposableтипівDisposeдзвонити кілька разів без помилок.Disposeметоду, використовуючи GC.SuppressFinalizeметодDisposeметодівУтилізація / доопрацьована модель
Disposeі Finalizeпід час роботи з некерованими ресурсами. FinalizeРеалізація буде працювати і ресурси будуть по- , як і раніше будуть випущені , коли об'єкт сміття , навіть якщо розробник забув викликати Disposeметод явно.Finalizeметоді, а також Disposeметоді. Додатково викличте Disposeметод для будь-яких об’єктів .NET, які у вас є компонентами всередині цього класу (маючи некеровані ресурси як їх член) з Disposeметоду.Завершити виклик GC, коли цей об'єкт більше не використовується.
Dispose - це просто звичайний метод, який користувач цього класу може закликати звільнити будь-які ресурси.
Якщо користувач забув зателефонувати Dispose і якщо у класі впроваджено Finalize, GC переконається, що він викликається.
У книжковій книжці MCSD Certification Toolkit (іспит 70-483) pag 193 є декілька ключів:
destructor ≈ (це майже дорівнює)base.Finalize() , Destructor перетворюється на версію заміщення методу Finalize, що виконує код деструктора, а потім викликає метод Finalize базового класу. Тоді його абсолютно не детермінований ви не можете знати, коли буде викликано, оскільки залежить від GC.
Якщо клас не містить керованих ресурсів і некерованих ресурсів , він не повинен реалізовувати IDisposableабо мати деструктор.
Якщо клас має лише керовані ресурси , він повинен реалізовуватись, IDisposableале він не повинен мати деструктора. (Коли деструктор виконується, ви не можете бути впевнені, що керовані об'єкти все ще існують, тому ви не можете викликати їхні Dispose()методи.)
Якщо клас має лише некеровані ресурси , він повинен реалізовуватись IDisposableта потребує деструктора у випадку, якщо програма не викликає Dispose().
Dispose()метод повинен бути безпечним для запуску не один раз. Ви можете досягти цього, використовуючи змінну, щоб відстежувати, чи вона була запущена раніше.
Dispose()повинні звільняти як керовані, так і некеровані ресурси .
Деструктор повинен звільняти лише некеровані ресурси . Коли деструктор виконує, ви не можете бути впевнені, що керовані об'єкти все ще існують, тому ви не можете викликати їхні способи розпорядження. Це отримується за допомогою канонічної protected void Dispose(bool disposing)схеми, коли звільняються (розпоряджаються) лише керовані ресурси disposing == true.
Після звільнення ресурсів Dispose()слід зателефонуватиGC.SuppressFinalize , щоб об’єкт міг пропустити чергу завершення.
Приклад реалізації для класу з некерованими та керованими ресурсами:
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
}
}
}
У 99% часу ви не повинні турбуватися ні про один. :) Але, якщо ваші об'єкти містять посилання на не керовані ресурси (наприклад, ручки вікон, ручки файлів), вам потрібно надати спосіб керованому об'єкту звільнити ці ресурси. Фіналізація дає неявний контроль над вивільненням ресурсів. Його називає сміттєзбірник. Розпорядження - це спосіб явного контролю над вивільненням ресурсів, і його можна викликати безпосередньо.
Про предмет збору сміття можна дізнатися набагато більше , але це початок.
Фіналізатор призначений для неявного очищення - ви повинні використовувати це щоразу, коли клас управляє ресурсами, які абсолютно повинні бути очищені, оскільки в іншому випадку ви просочилися б ручками / пам'яттю тощо ...
Правильно реалізувати фіналізатор, як відомо, важко, і його слід уникати, коли це можливо - SafeHandleклас (доступний у .Net v2.0 і вище) тепер означає, що вам дуже рідко (якщо і взагалі) потрібно більше застосовувати фіналізатор.
IDisposableІнтерфейс для явної очищення і набагато частіше використовується - ви повинні використовувати це , щоб дозволити користувачам явно звільнити або очищення ресурсів , коли вони закінчили з використанням об'єкта.
Зауважте, що якщо у вас є фіналізатор, ви також повинні реалізувати IDisposableінтерфейс, щоб дозволити користувачам явно звільнити ці ресурси раніше, ніж це було б, якби об’єкт збирав сміття.
Дивіться у розділі Оновлення ГД: Розпорядження, доопрацювання та управління ресурсами для того, що я вважаю найкращим та найповнішим набором рекомендацій щодо фіналізаторів та IDisposable.
Підсумок -
Крім того, ще одна відмінність полягає в тому, що в реалізації Dispose () ви також повинні випустити керовані ресурси , тоді як цього не слід робити у програмі Finalizer. Це тому, що дуже ймовірно, що керовані ресурси, на які посилається об'єкт, вже були очищені до готовності до завершення.
Для класу, який використовує некеровані ресурси, найкращою практикою є визначення обох - метод Dispose () та Finalizer - використовувати як резервний випадок, якщо розробник забуде явно розпоряджатися об'єктом. Обидва можуть використовувати спільний метод для очищення керованих та некерованих ресурсів: -
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
Найкращий приклад, який я знаю.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
Різниця між методами доопрацювання та розпорядження у C #.
GC закликає метод доопрацювання для відшкодування некерованих ресурсів (таких як операція файлів, windows api, підключення до мережі, підключення до бази даних), але час не визначається, коли GC називатиме його. Це називається неявно GC, це означає, що ми не маємо на ньому низького рівня контролю.
Спосіб утилізації: ми маємо на ньому низький рівень контролю, як називаємо це з коду. ми можемо відшкодувати некеровані ресурси, коли нам здається, що вони не є корисними. Ми можемо досягти цього, застосовуючи схему ідентифікації.
Екземпляри класу часто інкапсулюють контроль над ресурсами, якими не керує час виконання, наприклад ручками вікон (HWND), підключеннями до бази даних тощо. Тому ви повинні надати як явний, так і неявний спосіб звільнення цих ресурсів. Забезпечити неявний контроль, реалізуючи захищений метод Finalize на об'єкті (синтаксис деструктора в C # та керовані розширення для C ++). Колекціонер сміття викликає цей метод в якийсь момент після того, як більше немає дійсних посилань на об'єкт. У деяких випадках, можливо, ви хочете надати програмістам, що використовують об'єкт, можливість явно звільнити ці зовнішні ресурси, перш ніж збирач сміття звільнить об'єкт. Якщо зовнішній ресурс обмежений або дорогий, можна досягти кращої продуктивності, якщо програміст явно звільнить ресурси, коли вони більше не використовуються. Щоб забезпечити явний контроль, реалізуйте метод Dispose, передбачений інтерфейсом IDisposable. Споживач об'єкта повинен викликати цей метод, коли це робиться за допомогою об'єкта. Утилізацію можна назвати, навіть якщо інші посилання на об'єкт живі.
Зауважте, що навіть якщо ви надаєте явний контроль за допомогою Dispose, вам слід надати неявну очистку за допомогою методу Finalize. Finalize забезпечує резервну копію, щоб запобігти постійному витіканню ресурсів, якщо програміст не викличе Dispose.
Основна відмінність між розпорядженням та фіналізацією полягає в тому, що:
Disposeзазвичай викликається вашим кодом. Ресурси звільняються миттєво, коли ви його називаєте. Люди забувають називати метод, тому using() {}вигадка винайдена. Коли ваша програма закінчить виконання коду всередині {}, Disposeметод викличе автоматично.
Finalizeне називається вашим кодом. Це означає, щоб його викликали сміттєзбірник (GC). Це означає, що ресурс може бути звільнений у будь-який час у майбутньому, коли GC вирішить це зробити. Коли GC зробить свою роботу, вона пройде через багато методів Finalize. Якщо у вас є важка логіка в цьому, це зробить процес повільним. Це може спричинити проблеми з продуктивністю для вашої програми. Тому будьте уважні до того, що ви туди помістите.
Я особисто написав би велику частину логіки знищення в Dispose. Сподіваємось, це очистить плутанину.
Як ми знаємо, розпоряджаємось та доопрацьовуємо обидва використовуються для звільнення некерованих ресурсів .. але різниця полягає у доопрацюванні, використовуючи два цикли для звільнення ресурсів, де як розпорядження використовує один цикл ..
Щоб відповісти на першу частину, ви повинні навести приклади, коли люди використовують різний підхід для абсолютно одного класу-об’єкта. Інакше важко (або навіть дивно) відповісти.
Щодо другого питання, краще прочитайте спочатку це Правильне використання інтерфейсу IDisposable, який стверджує це
Це твій вибір! Але виберіть Розпорядження.
Іншими словами: GC знає лише про фіналізатор (якщо такий є. Також відомий як деструктор для Microsoft). Хороший код буде намагатися очистити з обох (фіналізатор та розпорядження).