Наследование и деструкторы в C # - PullRequest
12 голосов
/ 18 ноября 2011

Согласно this , в нем говорится, что Destructors cannot be inherited or overloaded. В моем случае для всех подклассов деструкторы будут идентичны. Значит ли это, что я должен определить один и тот же деструктор в каждом подклассе? Нет способа, которым я могу объявить деструктор в базовом классе и иметь дескриптор уничтожения? Скажем, у меня есть что-то вроде этого:

class A
{
    ~A()
    {
        SomethingA();
    }

}

class B : A
{

}

B b = new B();

Когда B уничтожен, его деструктор не будет вызван?

Ответы [ 7 ]

42 голосов
/ 18 ноября 2011

В соответствии с этим говорится, что деструкторы не могут быть унаследованы или перегружены.

Правильно. Деструкторы не являются наследуемыми членами, не являются виртуальными и поэтому не могут быть переопределены. Они всегда имеют одинаковую подпись, поэтому их нельзя перегружать.

В моем случае для всех подклассов деструкторы будут идентичны.

Тот факт, что вы задаете такой базовый вопрос, говорит мне, что вы не должны в первую очередь реализовывать деструктор. Правильная реализация деструктора - одна из самых сложных вещей в C # в все, кроме самых тривиальных случаев. Почему вы считаете, что вам нужно реализовать деструктор?

Значит ли это, что я должен определить один и тот же деструктор в каждом подклассе?

Нет, совсем нет. Как вы пришли к такому выводу из того факта, что деструкторы не наследуются?

Нет способа, которым я могу объявить деструктор в базовом классе и иметь дескриптор уничтожения?

Конечно, это разумно, при условии, что вы в первую очередь хотите реализовать деструктор.

Когда B уничтожен, его деструктор не будет вызван?

Это неправильно.

Мне приходит в голову, что вам потребовалось бы гораздо меньше времени, чтобы попробовать сами, чем задать здесь вопрос и дождаться ответа.

Когда деструкторов на самом деле вызывают? Это на сборке мусора, когда переменная выходит из области видимости?

Моя предыдущая гипотеза верна. Вы определенно не должны реализовывать деструктор до тех пор, пока не будете глубоко понимать весь процесс сбора мусора. Тот факт, что вы считаете, что переменные собираются, когда они выходят за рамки, например, указывает на то, что вы не понимаете этого достаточно глубоко, чтобы написать правильный деструктор.

Когда объект определен как недоступный из корня gc сборщиком, и объект имеет финализатор, который не был подавлен, тогда объект переводится в следующее поколение, помещая его в очередь завершения для обслуживания финализатор потока. Если нет, его память восстанавливается.

Когда поток финализатора начинает работать, он запускает все деструкторы объекта. (Деструкторы будут работать в порядке от большинства производных до наименее производных.) После этого процесса объект может быть недоступен или не быть доступным, а завершение может быть или не быть подавлено. Если объект определен как недоступный, весь процесс начинается снова.

Я не могу особо подчеркнуть, насколько хорошо вам нужно понимать процесс GC, чтобы сделать это правильно. Когда вы пишете деструктор, он работает в среде, где ничего не имеет смысла . Все ссылки в объекте могут относиться к объектам, которые имеют корни только в очереди финализатора; обычно все ссылки на живые вещи. Ссылки могут быть на объекты, которые уже завершены. Деструкторы работают в другом потоке. Деструкторы запускаются даже в случае сбоя конструктора, поэтому объект может даже не быть сконструирован должным образом. Поля неатомарных типов значений могут быть записаны только частично - для двойного поля вполне возможно иметь только четыре своих байта, установленных конструктором при прерывании потока; финализатор увидит это частично написанное поле. Деструкторы запускаются, даже если объект был переведен в несогласованное состояние прерванной транзакцией. И так далее. Вы должны быть крайне оборонительными при написании деструктора.

Этот ответ также может помочь:

Когда мне создавать деструктор?

18 голосов
/ 18 ноября 2011

Это не деструктор в C #.Это известно как Finializer;и когда это называется недетерминированным.Вы на самом деле не можете рассчитывать на то, что его вызовут вообще.

Финализаторы используются в качестве последнего средства для очистки неуправляемых ресурсов.Вы должны изучить шаблон Dispose .

7 голосов
/ 18 ноября 2011

Финализатор, который вы определили в A, будет вызываться при уничтожении экземпляра B.

Если вы определите финализатор как в A, так и в B, сначала будет запущен самый специфический финализатор (B), затемнаименее конкретный (A).

2 голосов
/ 18 ноября 2011

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

0 голосов
/ 15 октября 2018

Быстрое консольное приложение может помочь проверить подобные вещи.

using System;

class A
{
    ~A() => Console.WriteLine("~A");       
}

class B : A
{
    ~B() => Console.WriteLine("~B");
}

public class Program
{
    public static void Main() => new B();        
}

Вывод может быть ...

~B
~A
0 голосов
/ 18 ноября 2011

Ну, я не знаю о деструкторах, но у вас есть другие полезные методы для очистки, такие как Finalize () и Dispose () из IDisposable.

0 голосов
/ 18 ноября 2011

Если вы определите деструктор для B, он будет вызван, а затем A.См. Пример внизу предоставленной вами ссылки .

...