Методы Dispose, финализаторы и деструкторы 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
(или объявлять "деструкторы") только в относительно простых классах.
person
supercat
schedule
04.01.2012