Когда я должен создать деструктор?

185

Например:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

Когда я должен вручную создать деструктор? Когда вам нужно было создать деструктор?

Дэвид Хеффернан
источник
1
Язык C # называет эти «деструкторы», но большинство людей называют их «финализаторами», так как это их имя .NET, и это уменьшает путаницу с деструкторами C ++ (которые весьма различны). Как реализовать IDisposable и финализаторы: 3 простых правила
Стивен Клири
5
Когда вы чувствуете себя безрассудным.
Капдрагон
32
Я думаю, что клавиатура TomTom работала со сбоями. Замок с крышками спорадически включался и выключался. Weird.
Джефф ЛаФай
смотри также stackoverflow.com/questions/1076965/...
Ian Рингроуза
В итоге я использовал деструктор в качестве средства отладки, основываясь на предложении Грега Бича: stackoverflow.com/questions/3832911/…
Брайан,

Ответы:

237

ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога в мае 2015 года . Спасибо за отличный вопрос! Смотрите в блоге длинный список лжи, которые люди обычно верят в финализацию.

Когда я должен вручную создать деструктор?

Почти никогда.

Обычно один создает деструктор только тогда, когда ваш класс держится за какой-то дорогой неуправляемый ресурс, который должен быть очищен, когда объект исчезает. Лучше использовать одноразовый шаблон, чтобы обеспечить очистку ресурса. Деструктор - это, по сути, гарантия того, что, если потребитель вашего объекта забудет его утилизировать, ресурс все равно будет очищен. (Может быть.)

Если вы делаете деструктор, будьте предельно осторожны и понимайте, как работает сборщик мусора . Деструкторы являются действительно странно :

  • Они не работают на вашей нити; они бегут в своей собственной нити. Не вызывайте тупиков!
  • Необработанное исключение, создаваемое деструктором, является плохой новостью. Это на своей собственной нити; кто поймает это?
  • Деструктор может быть вызван для объекта после запуска конструктора, но до его завершения. Правильно написанный деструктор не будет полагаться на инварианты, установленные в конструкторе.
  • Деструктор может «воскресить» объект, оживив мертвый объект. Это действительно странно. Не делай этого.
  • Деструктор может никогда не бежать; Вы не можете полагаться на объект, когда-либо запланированный для завершения. Это , вероятно , будет, но это не является гарантией.

Почти все, что обычно верно, верно для деструктора. Будьте очень, очень осторожны. Написание правильного деструктора очень сложно.

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

При тестировании части компилятора, которая обрабатывает деструкторы. Мне никогда не нужно было делать это в рабочем коде. Я редко пишу объекты, которые манипулируют неуправляемыми ресурсами.

Эрик Липперт
источник
«Деструктор может быть вызван для объекта после запуска конструктора, но до его завершения». Но я могу рассчитывать на запуск полевых инициализаторов, верно?
конфигуратор
13
@configurator: Нет. Предположим, что инициализатор третьего поля объекта с финализатором называется статическим методом, который вызвал исключение. Когда будет запущен четвертый инициализатор поля? Никогда. Но объект все еще выделен и должен быть завершен. Черт возьми, у вас даже нет гарантии, что поля типа double были полностью инициализированы при запуске dtor. Возможно, поток был прерван в середине записи двойного числа, и теперь финализатору приходится иметь дело с полуинициализированным половинным нулем.
Эрик Липперт
1
Отличный пост, но он должен был сказать «должен быть создан, когда ваш класс держится за какой-то дорогой неуправляемый объект или вызывает существование большого количества неуправляемых объектов». Для конкретного примера у меня есть матричный класс в C #, который использует базовый нативный C ++ класс матрицы, чтобы сделать много тяжелой работы - я делаю много матриц - «деструктор» намного превосходит IDisposable в этом конкретном случае, потому что он держит управляемые и неуправляемые стороны дома в лучшей синхронизации
Марк Муллин
1
pythonnet использует деструктор для выпуска GIL в неуправляемом CPython
denfromufa
3
Крутая статья Эрика. Причины для этого -> "Дополнительный бонус: при запуске программы в отладчике среда выполнения использует менее агрессивную генерацию кода и менее агрессивную сборку мусора, поскольку отладка объектов, отлаживаемых вами, внезапно исчезает, даже если переменная, относящаяся к объекту, находится в области видимости. Это означает, что если у вас есть ошибка, когда объект завершается слишком рано, вы, вероятно, не сможете воспроизвести эту ошибку в отладчике! "
Кен Палмер
17

Он называется «финализатор», и вам обычно следует создавать его только для класса, состояние которого (например, поля) включает неуправляемые ресурсы (т. Е. Указатели на дескрипторы, полученные с помощью вызовов p / invoke). Однако в .NET 2.0 и более поздних версиях существует более эффективный способ очистки неуправляемых ресурсов: SafeHandle . Учитывая это, вам больше не нужно писать финализатор снова.

Николь Калиною
источник
25
@ThomasEding - Да, это так . C # использует синтаксис деструктора, но фактически создает финализатор . Опять же .
JDB до сих пор помнит Монику
@JDB: лингвистическая конструкция называется деструктором. Мне не нравится это имя, но оно так и называется. При объявлении деструктора компилятор генерирует метод финализатора, который содержит немного кода-обертки вместе с тем, что появляется в теле деструктора.
суперкат
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
Ойвинд Бротен,
1
@TomTom это официальное имя деструктор
Дэвид Хеффернан
На самом деле это не резервный метод, он просто позволяет GC управлять, когда ваши объекты освобождают неуправляемые ресурсы, а реализация IDisposable позволяет вам управлять этим самостоятельно.
HasaniH
3

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

Влад Безден
источник
2

Я использовал деструктор (только для целей отладки), чтобы посмотреть, удалялся ли объект из памяти в области видимости приложения WPF. Я был не уверен, действительно ли сборщик мусора удалял объект из памяти, и это был хороший способ проверить.

Neil.Allen
источник
1

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

HasaniH
источник