Коли я повинен створити деструктор?


185

Наприклад:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Коли я повинен вручну створити деструктор? Коли вам потрібно було створити деструктор?


1
Мова C # називає цих "деструкторів", але більшість людей називають їх "фіналізаторами", оскільки це їх ім'я .NET, і це зменшує плутанину з деструкторами C ++ (які зовсім інші). Як реалізувати ідентифікатори та фіналізатори: 3 простих правила
Стівен Клірі

5
Коли ти почуваєшся безрозсудливим.
Капдрагон

32
Я думаю, що клавіатура TomTom несправна. Блокування кришок спорадично відбувається і вимикається. Дивно.
Jeff LaFay


Я в кінцевому підсумку використовував деструктор як допомогу налагодження на основі пропозиції Грега Бука: stackoverflow.com/questions/3832911/…
Брайан

Відповіді:


237

ОНОВЛЕННЯ: Це питання було предметом мого блогу у травні 2015 року . Дякую за чудове запитання! Подивіться у блозі довгий список неправдивих дій, які люди зазвичай вірять у доопрацюванні.

Коли я повинен вручну створити деструктор?

Майже ніколи.

Зазвичай деструктор створює лише тоді, коли ваш клас тримається за якийсь дорогий некерований ресурс, який необхідно очистити, коли об’єкт відходить. Краще скористатися одноразовою схемою, щоб забезпечити очищення ресурсу. Тоді деструктор - це, по суті, гарантія того, що якщо споживач вашого об'єкта забуде його утилізувати, ресурс все-таки очищається. (Може бути.)

Якщо ви робите деструктор, будьте дуже обережні і розумієте, як працює сміттєзбірник . Деструктори справді дивні :

  • Вони не працюють на вашій нитці; вони бігають по власній нитці. Не спричиняйте тупиків!
  • Неопрацьований виняток, викинутий з деструктора - це погані новини. Це на власній нитці; хто збирається його спіймати?
  • Деструктор може бути викликаний на об'єкті після запуску конструктора, але до закінчення конструктора. Правильно написаний деструктор не покладається на інваріантів, встановлених у конструкторі.
  • Деструктор може «воскресити» об’єкт, знову зробивши мертвий об’єкт живим. Це справді дивно. Не робіть цього.
  • Деструктор ніколи не може працювати; ви не можете покластися на об'єкт, який коли-небудь планується доопрацювати. Це , ймовірно , буде, але це не є гарантією.

Практично нічого, що зазвичай є правдою, не відповідає справжньому деструктору. Будьте справді, дуже обережні. Написати правильний деструктор дуже складно.

Коли вам потрібно було створити деструктор?

При тестуванні частини компілятора, яка обробляє деструктори. Мені ніколи не потрібно було це робити у виробничому коді. Я рідко пишу об’єкти, які маніпулюють некерованими ресурсами.


"Деструктор може бути викликаний на об'єкті після запуску конструктора, але до закінчення конструктора." Але я можу розраховувати на те, що запустити ініціалізатори поля, правда?
конфігуратор

13
@configurator: Ні. Припустимо, ініціалізатор третього поля об'єкта з фіналізатором називається статичним методом, який спричинив викид виключення. Коли запустився б ініціалізатор четвертого поля? Ніколи. Але об’єкт все-таки виділяється і його необхідно доопрацювати. Чорт, ви навіть не маєте гарантії, що поля подвійного типу були повністю ініціалізовані при запуску dtor. Можливо, нитка перервалася на півдорозі через написання подвійного, і тепер фіналізатор має мати справу з напівініціалізованим подвійним подвійним подвійним.
Ерік Ліпперт

1
Відмінний пост, але, мабуть сказати, "його слід створити, коли ваш клас тримається за якийсь дорогий некерований об'єкт або спричиняє існування великої кількості некерованих об'єктів" - На конкретному прикладі у мене є клас матриць в C #, який використовує основний основний C ++ клас матриць, щоб зробити багато важких підйомів - я роблю багато матриць - "деструктор" набагато перевершує ID, який використовується в цьому конкретному випадку, тому що він підтримує керовані та некеровані сторони будинку в кращій синхронізації
Марк Маллін

1
pythonnet використовує деструктор для випуску GIL у некерованому CPython
denfromufa

3
Дивовижна стаття Еріка. Реквізити для цього -> "Додаткове задоволення від бонусів: під час виконання програми в налагоджувачі використовується менш агресивне генерування коду та менш агресивне збирання сміття, оскільки це поганий досвід налагодження, коли об'єкти, які ви налагоджуєте, раптом зникають, хоча змінна, що посилається на об'єкт, знаходиться в області застосування. Це означає, що якщо у вас є помилка, коли об'єкт фіналізується занадто рано, ви, ймовірно, не можете відтворити цю помилку в налагоджувачі! "
Кен Палмер

17

Це називається "фіналізатор", і зазвичай вам слід створити лише той клас, стан якого (тобто: поля) включає некеровані ресурси (тобто: покажчики на обробку, отримані за допомогою викликів p / invoke). Однак у .NET 2.0 та пізніших версіях дійсно є кращий спосіб впоратися з очищенням некерованих ресурсів: SafeHandle . Враховуючи це, вам майже ніколи не потрібно писати фіналізатор знову.


25
@ThomasEding - Так . C # використовує синтаксис деструктора, але він фактично створює фіналізатор . Знову .
JDB досі пам’ятає Моніку

@JDB: Мовна конструкція називається деструктором. Мені це не подобається, але це так називається. Акт оголошення деструктора змушує компілятор створити метод фіналізатора, який містить трохи коду обгортки разом із тим, що з’явиться в тілі деструктора.
Supercat

8

Він вам не потрібен, якщо ваш клас не підтримує керовані ресурси, наприклад, файли Windows.


5
Ну, насправді, це називається деструктором
Девід Геффернан

2
Тепер я розгублений. Це фіналізатор чи деструктор?

4
Специфікація C # насправді називає це руйнівником. Деякі вбачають це як помилку. stackoverflow.com/questions/1872700/…
Ані

2
@ThomasEding - Так . C # використовує синтаксис деструктора, але він фактично створює фіналізатор .
JDB досі пам’ятає Моніку

2
Я люблю коментарі тут, справжній панто :)
Benjol

4

Він називається деструктором / фіналізатором і зазвичай створюється при реалізації шаблону Disposed.

Це резервне рішення, коли користувач вашого класу забуває зателефонувати на Dispose, щоб переконатися, що (зрештою) ваші ресурси будуть звільнені, але ви не маєте жодної гарантії, коли викликається деструктор.

У цьому питанні щодо переповнення стека прийнята відповідь правильно показує, як реалізувати схему розпорядження. Це потрібно лише в тому випадку, якщо ваш клас містить будь-які незроблені ресурси, які сміттєзбірник не вдається самостійно очистити.

Доброю практикою є не реалізація фіналізатора, не надаючи користувачеві класу можливість вручну розпоряджатись об'єктом, щоб негайно звільнити ресурси.


Насправді його НЕ називають деструктором у C # з поважною причиною.
TomTom

14
Насправді так і є . Дякую за те, що ви дали мені протокол, оскільки ви помиляєтесь. Дивіться бібліотеку MSDN щодо цієї конкретної проблеми: msdn.microsoft.com/en-us/library/66x5fx1b.aspx
Øyvind Bråthen

1
@TomTom Офіційне ім'я деструктора
Девід Хеффернан

Це насправді не є резервним методом, він просто дозволяє GC керувати, коли ваші об’єкти звільняють некеровані ресурси, реалізуючи IDisposable, ви можете керувати цим самостійно.
HasaniH

3

Якщо у вас є некеровані ресурси, вам потрібно переконатися, що вони будуть очищені, коли ваш об’єкт згасне. Хорошим прикладом можуть бути об’єкти COM або файлові обробники.


2

Я використовував деструктор (лише для налагодження), щоб перевірити, чи чистий об'єкт очищено з пам'яті в рамках програми WPF. Я не знав, чи збирання сміття справді очищає об'єкт із пам'яті, і це був хороший спосіб перевірити.


1

Деструктори пропонують неявний спосіб звільнення некерованих ресурсів, інкапсульованих у вашому класі, вони викликаються, коли GC обійдеться до нього, і вони неявно викликають метод Finalize базового класу. Якщо ви використовуєте багато некерованих ресурсів, краще надати чіткий спосіб звільнення цих ресурсів через інтерфейс IDisposable. Дивіться посібник з програмування C #: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.