В C # в чем разница между деструктором и методом Finalize в классе?

98

В чем разница, если она есть, между деструктором и методом 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

Деструктор в C # переопределяет System.Object.Finalizeметод. Для этого вы должны использовать синтаксис деструктора. При переопределении вручную выдается Finalizeсообщение об ошибке.

В основном то, что вы пытаетесь сделать с Finalizeобъявлением вашего метода, скрывает метод базового класса. Это приведет к тому, что компилятор выдаст предупреждение, которое можно отключить с помощью newмодификатора (если это будет работать). Здесь важно отметить, что вы не можете одновременно overrideобъявить newчлен с одинаковым именем и деструктор, и Finalizeметод, что приведет к ошибке (но вы можете , хотя и не рекомендуется, объявить public new void Finalize()метод, если вы не объявляете деструктор).

Мехрдад Афшари
источник
71

В Википедии есть хорошее обсуждение разницы между финализатором и деструктором в статье о финализаторе .

В C # действительно нет "настоящего" деструктора. Синтаксис похож на деструктор C ++, но на самом деле это финализатор. Вы правильно написали это в первой части своего примера:

~ClassName() { }

Это синтаксический сахар для Finalizeфункции. Это гарантирует, что финализаторы в базе гарантированно работают, но в остальном идентично переопределению Finalizeфункции. Это означает, что когда вы пишете синтаксис деструктора, вы действительно пишете финализатор.

Согласно Microsoft , финализатор относится к функции, которую сборщик мусора вызывает, когда он собирает ( Finalize), в то время как деструктор - это ваш фрагмент кода, который выполняется в результате (синтаксический сахар, который становится Finalize). Они настолько близки к тому, чтобы быть одним и тем же, что Microsoft никогда не следовало делать различия.

Использование Microsoft термина «деструктор» C ++ вводит в заблуждение, потому что в C ++ он выполняется в том же потоке, как только объект удаляется или выталкивается из стека, тогда как в C # он выполняется в отдельном потоке в другое время.

Kenzi
источник
Я бы сказал, что такое различие между деструктором и финализатором важно проводить. Однако только те, кто заботится о том, что происходит под капотом, будут заботиться об указанном различии.
Кайл Баран
1
Также обратите внимание, что ECMA-334 уже давно формально устраняет неоднозначность «деструктор» и «финализатор». Я не знаю, почему MS до сих пор настаивает на вводящем в заблуждение термине в своих спецификациях.
FrankHB
По крайней мере, из-за работы с Mono, C # фактически смоделирован после C ++, и большинство собственных объектов C # являются объектами C ++. То, как работает компилятор, скомпилировавший Mono, определяет, как эти объекты C ++ разрушаются, а также то, как финализация объекта C # распространяется до C ++ и вызывает эти деструкторы. Это различие имеет смысл под капотом, но на самом деле оно не относится к самому C #.
Kenzi
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 мы пишем код очистки для объекта. Важно, чтобы мы освободили все неуправляемые ресурсы в методе удаления, такие как соединение с базой данных, файлы и т. Д. Класс, реализующий метод удаления, должен реализовывать интерфейс IDisposable. Метод Dispose должен вызывать метод GC.SuppressFinalize для объекта, который он удаляет, если У класса есть дестуректор, потому что он уже выполнил работу по очистке объекта, то сборщику мусора нет необходимости вызывать метод Finalize объекта. Ссылка: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Завершить

    Метод Finalize действует как средство защиты для очистки ресурсов в случае, если ваш метод Dispose не вызывается. Вы должны реализовать метод Finalize только для очистки неуправляемых ресурсов. Вы не должны реализовывать метод 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> - гораздо более узкий сценарий.
supercat
@hqt: если один класс будет напрямую владеть неуправляемыми ресурсами, а также содержит ссылки на другие объекты, неуправляемые ресурсы обычно должны быть разделены на их собственный финализируемый класс (который в идеале не должен содержать никаких сильных ссылок на что-либо еще), то есть класс который содержит ссылки на другие объекты, будет владеть только «вещами, которые напрямую владеют неуправляемыми ресурсами», а не самими ресурсами, и поэтому не будет нуждаться в финализаторе.
supercat