Відповіді:
Інші вже покрили різницю між 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). Хороший код буде намагатися очистити з обох (фіналізатор та розпорядження).