Используются ли методы деструктора неявным образом сборщиком мусора и располагают методы, используемые разработчиками для явного удаления объектов? - PullRequest
3 голосов
/ 04 января 2012

Я вижу, что уже есть много потоков, касающихся методов утилизации и деструктора, но я просто хочу убедиться, что я правильно понимаю их, прежде чем я продолжу.

Методы деструктора неявно используютсясборщик мусора, когда на объекты больше нет ссылок (т.е. они больше не нужны) и используются методы, используемые разработчиками для явного удаления объектов, которые не могут быть обработаны сборщиком мусора?

Также - я читаю ввсе это сейчас, и кажется, что это тот или иной случай с этими методами.Так, например, с учетом следующего кода:

class DansClass : IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Console.WriteLine("Disposing...");
    }

    -DansClass()
    {
        Console.WriteLine("Destructing...");
    }
}

Вывод будет:

Уничтожение ...

Понятно, как мыподавил финализацию (деструктор), поэтому мы видим только вывод Dispose.

Но если я закомментирую метод SuppressFinalize (), вывод будет:

Disposing ...

Почему деструктор тоже не называется?

Ответы [ 2 ]

6 голосов
/ 04 января 2012

Хорошо, вы видите поведение, потому что Финализаторы (то, что вы называете Деструктором, также называется Финализатором в .Net), стоят в очереди, чтобы работать в фоновом режиме с помощью Мусора.Collector.

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

// Assuming you remove the GC.SuppressFinalize call
myDisposable.Dispose();

GC.WaitForPendingFinalizers();
// Output:
// Disposing...
// Destructing...

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

Для правильной реализации шаблона IDisposable требуется:

  • Dispose очищает неуправляемые и управляемые ресурсы
  • Финализатор убирает зависания, неуправляемые ресурсы (если их нет, они не нужны)
  • Никто не может выдать исключение, Dispose должен вызываться более одного раза
  • Финализатор должен вызывать Dispose реализацию
  • Специфичный для домена завершение методы, например Close, должен быть функционально эквивалентен Dispose, если оба существуют
1 голос
/ 04 января 2012

Методы удаления, финализаторы и деструкторы с ироническим именем в C # существуют потому, что многие объекты просят другие объекты делать что-то от их имени до дальнейшего уведомления (как правило, предоставляя исключительное использование чего-то вроде файла, области памяти, потока связи, аппаратное устройство, дескриптор GDI и т. д.); если объект, выдающий такой запрос, исчезнет без того, чтобы другие объекты не знали, что их услуги больше не требуются, все, что было отложено от имени оставленного объекта, останется бесполезно недоступным.

Метод IDisposable.Dispose обеспечивает хороший последовательный способ сообщить объекту, что от него больше не будет требоваться что-либо делать, потребуется помощь других лиц, и что любые другие объекты, которые делают что-либо от его имени, должны быть уведомлены прекратить это делать. Если IDisposable.Dispose вызывается правильно, объекты могут минимизировать степень, в которой внешние объекты должны действовать от их имени.

К сожалению, по разным причинам (большинство из них легко избежать; некоторые нет), объекты иногда оставляются без вызова IDisposable.Dispose. Это приведет к тому, что сторонним организациям придется бесполезно продолжать действовать, по крайней мере, некоторое время, от имени оставленных объектов. Чтобы не допустить, чтобы внешние сущности действовали вечно от имени внешних сущностей, система может уведомлять объекты о том, что они были оставлены, что дает им возможность информировать внешние сущности об этом факте. Всякий раз, когда создается объект, класс которого переопределяет Object.Finalize, он будет помещен в специальный список объектов, которые хотят получать уведомления, если они были оставлены. Само по себе наличие в этом списке недостаточно для того, чтобы объект считался «живым», но перед тем, как сборщик мусора удалит мертвые объекты из памяти, он проверит, находятся ли они в списке уведомлений перед уничтожением. Все мертвые объекты, которые есть в списке, будут перемещены из списка объектов, запрашивающих уведомление, если / когда они будут оставлены, в список объектов, нуждающихся в уведомлении о том, что они имеют . Размещение в этом втором списке приведет к тому, что мертвые объекты и любые объекты, на которые они ссылаются , снова будут считаться «живыми», по крайней мере, до тех пор, пока не будет выполнено уведомление. Однако когда они перемещаются во второй список, они удаляются из первого списка, так что, если впоследствии они обнаружат, что они снова мертвы, они будут удалены из памяти без дальнейшего уведомления.

После завершения сборки мусора, если какие-либо объекты находятся в списке объектов, о которых необходимо уведомить об отказе, система вызовет метод Object.Finalize для каждого такого объекта. Как правило, после вызова Object.Finalize для такого объекта больше не будет корневых ссылок на него, и он исчезнет при следующей сборке мусора. Однако возможно возрождение объектов.

В vb.net и, насколько мне известно, в большинстве языков .net один переопределяет Object.Finalize, просто объявляя переопределение обычным способом. По какой-то причине создатели C # решили запретить это. Вместо этого в C # необходимо использовать языковую структуру, по иронии судьбы называемую «Разрушитель», чтобы переопределить Finalize, чтобы объекты, которые были найдены как оставленные, не были уничтожены без уведомления, а вместо этого были заданы шанс навести порядок.

В реальной операции Object.Finalize есть много хитрых морщин, и лучше избегать полагаться на это, кроме случаев, когда это абсолютно необходимо. Иронически названные «деструкторы» на самом деле не разрушают объекты, а задерживают их разрушение. Вызов GC.SuppressFinalize() для объекта удалит его из списка объектов, запрашивающих уведомление, когда они будут оставлены; при условии, что Dispose вызывается для объекта, и он, в свою очередь, вызывает GC.SuppressFinalize() как свою последнюю операцию, нет особого вреда в том, что объект переопределил Finalize или объявил "деструктор" ", но в общем случае лучше переопределить Finalize (или объявить" деструкторы ") в относительно простых классах.

...