У C # яка різниця між деструктором та методом Finalize у класі?


97

Яка різниця, якщо існує один, між деструктором та методом Finalize у класі?

Нещодавно я виявив, що Visual Studio 2008 вважає деструктор синонімом методу Finalize, тобто Visual Studio не дозволяє одночасно визначати обидва методи в класі.

Наприклад, наступний фрагмент коду:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Показує таку помилку під час виклику Finalize у деструкторі:

Виклик неоднозначний між такими методами або властивостями: 'TestFinalize. ~ TestFinalize ()' та 'TestFinalize.Finalize ()'

І якщо виклик Finalize буде прокоментовано, він видає таку помилку:

Тип 'ManagementConcepts.Service.TestFinalize' вже визначає учасника з назвою 'Finalize' з тими самими типами параметрів

Відповіді:


68

Деструктор System.Object.Finalizeметодом C # переосмислює . Для цього вам потрібно використовувати синтаксис деструктора. Перейменування вручну Finalizeпризведе до повідомлення про помилку.

В основному те, що ви намагаєтеся зробити з Finalizeдекларацією методу, приховує метод базового класу. Це призведе до того, що компілятор видасть попередження, яке можна заглушити за допомогою newмодифікатора (якщо він буде працювати). Тут важливо зазначити, що ви не можете одночасно overrideі оголошувати newчлена з однаковим іменем, тому наявність деструктора і Finalizeметоду призведе до помилки (але ви можете , хоча і не рекомендується, оголосити public new void Finalize()метод, якщо ви не декларуєте деструктора).


71

У Вікіпедії є гарне обговорення різниці між фіналізатором і деструктором у статті про фіналізатор .

C # дійсно не має "справжнього" деструктора. Синтаксис нагадує деструктор C ++, але він справді є фіналізатором. Ви правильно написали це в першій частині свого прикладу:

~ClassName() { }

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

На думку Microsoft , фіналізатор посилається на функцію, яку викликає збирач сміття, коли збирає ( Finalize), тоді як деструктор - це ваш біт коду, який виконується в результаті (синтаксичний цукор, який стає Finalize). Вони настільки близькі до того самого, що Microsoft ніколи не повинна була розрізняти.

Використання Microsoft терміну "деструктор" C ++ вводить в оману, оскільки в C ++ він виконується на одній темі, як тільки об'єкт видаляється або вискакує з стека, в той час як в C # він виконується на окремому потоці в інший час.


Я б стверджував, що таке розмежування деструктора та фіналізатора є важливим. Однак про ці відмінності потурбуються лише ті, хто дбає про те, що відбувається під кришкою.
Кайл Баран

1
Також зауважте, що ECMA-334 вже давно чітко розмежував "деструктор" та "фіналізатор". Я не знаю, чому РС все ще наполягає на оманливих термінах у своїх специфікаціях.
FrankHB

Принаймні, з роботи з Mono, C # насправді моделюється після C ++, а більшість власних об'єктів C # - це об'єкти C ++. Те, як працює компілятор, який компілював Mono, диктує, як руйнуються ці об'єкти C ++, а також, як фіналізація об'єкта C # поширюється до C ++ та викликає цих деструкторів. Відмінність має сенс під кришкою, але вона все ще не стосується самого C #.
Кензі

20

Знайдено тут: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Деструктор

    Це спеціальні методи, що містять код очищення для об'єкта. Ви не можете викликати їх явно у своєму коді, оскільки вони неявно викликаються GC. У C # вони мають те саме ім’я, що і назва класу, що передує ~знаку. Подібно до-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }
    

    У VB.NET деструктори реалізовані шляхом заміни методу Finalize класу System.Object.

  2. Утилізувати

    Це так само, як і будь-які інші методи в класі, і їх можна назвати явно, але вони мають спеціальну мету очищення об'єкта. У методі dispose ми пишемо код очищення об'єкта. Важливо, щоб ми звільнили всі некеровані ресурси в методі розпорядження, наприклад, підключення до бази даних, файли і т.д. клас має desturctor, оскільки він вже виконав роботу з очищення об'єкта, тоді збирачу сміття не потрібно викликати метод Finalize об'єкта. Довідка: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Довершити

    Метод Finalize діє як захист для очищення ресурсів у тому випадку, якщо ваш метод Dispose не викликається. Вам слід застосовувати лише метод Finalize для очищення некерованих ресурсів. Не слід застосовувати метод Finalize для керованих об’єктів, оскільки збирач сміття автоматично очищає керовані ресурси. Метод завершення викликається GC неявно, тому ви не можете викликати його зі свого коду.

    Примітка. У C # метод Finalize не може бути переопрацьований, тому вам доведеться використовувати деструктор, внутрішня реалізація якого перекриє метод Finalize в MSIL. Але у VB.NET метод Finalize може бути замінений, оскільки він підтримує метод деструктора.

Оновлення: Цікава напівспоріднена нитка тут .


1
You should only implement a Finalize method to clean up unmanaged resources: ви помістили це у Finalize. Те саме з Dispose?
hqt

@hqt: Випадки, коли потрібно Disposeзначно перевищувати кількість тих, де слід застосувати фіналізатор. Реалізуйте, Disposeякщо є ймовірність, що екземпляр класу або похідного класу буде останньою справою або безпосередньо володіти некерованим ресурсом, або безпосередньо володіти останньою справою безпосередньо володіти некерованим ресурсом, або безпосередньо володіти останньою річчю, безпосередньо володіти і т. д. Реалізувати Finalizeдля очищення ресурсів можна лише в тому випадку, якщо клас <i> <i> безпосередньо </i> володіє некерованим ресурсом <i> і майже нічого іншого </i> - набагато вужчим сценарієм.
supercat

@hqt: Якщо один клас безпосередньо володіє некерованими ресурсами, а також містить посилання на інші об'єкти, некеровані ресурси, як правило, повинні бути розділені на власний остаточний клас (який в ідеалі не повинен містити жодних чітких посилань ні на що інше), тобто клас який містить посилання на інші об'єкти, матиме лише "речі, які безпосередньо володіють некерованими ресурсами", а не володіє самими ресурсами, і тому не потребує доопрацювання.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.