Время существования: создание и уничтожение объектов (Visual Basic)

Экземпляр класса, объект, создается с помощью ключевого слова New. Задачи инициализации зачастую необходимо выполнять на новых объектах до их использования. К распространенным задачам инициализации относится открытие файлов, подключение к базам данных и чтение значений параметров реестра. Visual Basic управляет инициализацией новых объектов с помощью процедур, называемых конструкторами (специальные методы , которые позволяют контролировать инициализацию).

Когда объект выходит из области, он высвобождается средой CLR. Visual Basic управляет выпуском системных ресурсов с помощью процедур, называемых деструкторами. Вместе конструкторы и деструкторы поддерживают создание надежных и предсказуемых библиотек класса.

Использование конструкторов и деструкторов

Конструкторы и деструкторы управляют созданием и уничтожением объектов. Sub Finalize Процедуры Sub New в Visual Basic инициализировать и уничтожить объекты; они заменяют Class_InitializeClass_Terminate методы, используемые в Visual Basic 6.0 и более ранних версиях.

Конструктор Sub New

Конструктор Sub New может быть запущен только один раз при создании класса. Его нельзя вызвать явным образом нигде, кроме первой строки кода другого конструктора этого же класса или производного класса. Более того, код метода Sub New всегда выполняется до любого другого кода в классе. Visual Basic неявно создает Sub New конструктор во время выполнения, если явно не определяется Sub New процедура для класса.

Чтобы создать конструктор класса, создайте процедуру с именем Sub New в любом месте определения класса. Чтобы создать конструктор с параметрами, укажите имена и типы данных аргументов в Sub New точно так же, как для любой процедуры. См. следующий код:

Sub New(ByVal s As String)

Конструкторы часто перегружены, как в следующем коде:

Sub New(ByVal s As String, i As Integer)

При вызове класса, производного от другого класса, первая строка конструктора должна представлять собой вызов конструктора базового класса (кроме случаев, когда в базовом классе есть доступный конструктор, не принимающий параметры). Вызов базового класса, содержащего указанный выше конструктор, может быть, к примеру, таким MyBase.New(s). MyBase.New В противном случае является необязательным, а среда выполнения Visual Basic вызывает ее неявно.

После написания кода для вызова конструктора родительского объекта можно добавить дополнительный код инициализации к процедуре Sub New. Sub New может принимать аргументы при вызове в качестве конструктора с параметрами. Эти параметры передаются из процедуры, вызывающей конструктор, например, Dim AnObject As New ThisClass(X).

Sub Finalize

Перед высвобождением объектов среда CLR автоматически вызывает метод Finalize для объектов, определяющих процедуру Sub Finalize. Метод Finalize может содержать код, который необходимо выполнить непосредственно перед уничтожением объекта, например, код для закрытия файлов и сохранения информации о состоянии. Существует небольшой спад производительности при выполнении Sub Finalize, поэтому метод Sub Finalize нужно определять только в тех случаях, когда требуется явное высвобождение объектов.

Примечание.

Сборщик мусора в среде CLR не удаляет неуправляемые объекты, объекты, выполняемые операционной системой непосредственно за пределами среды CLR. Причина состоит в том, что разные неуправляемые объекты следует уничтожать по-разному. Эта информация не связана напрямую с неуправляемым объектом, ее необходимо найти в документации по объектам. Класс, использующий неуправляемые объекты, должен удалить их в своем методе Finalize.

Деструктор Finalize является защищенным методом, который можно вызвать только из класса, к которому он принадлежит, или из производного класса. Система автоматически вызывает Finalize при уничтожении объекта, поэтому не следует явным образом вызывать Finalize извне реализации Finalize производного класса.

В отличие от Class_Terminate, выполняющегося сразу же при уничтожении объекта, обычно существует пауза между потерей объектом области и вызовом деструктора Finalize в Visual Basic. Visual Basic .NET позволяет использовать второй тип деструктора, IDisposable.Disposeкоторый можно явно вызывать в любое время для немедленного выпуска ресурсов.

Примечание.

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

Как методы New и Finalize работают в иерархии классов

При каждом создании экземпляра класса среда CLR пытается выполнить процедуру New, если она существует в этом объекте. New — тип процедуры, которая называется constructor и используется для инициализации новых объектов до выполнения всего остального кода в объекте. Конструктор New можно использовать для открытия файлов, подключения к базам данных, инициализации переменных и для других задач, которые необходимо выполнить перед использованием объекта.

Когда создается экземпляр производного класса, конструктор Sub New базового класса выполняется в первую очередь, а затем — конструкторы в производных классах. Это происходит, поскольку первая строка кода в конструкторе Sub New использует синтаксис MyBase.New() для вызова конструктора класса на один уровень выше себя в иерархии классов. Затем конструктор Sub New вызывается для каждого класса в иерархии вплоть до достижения базового класса. На этом этапе выполняется код в конструкторе базового класса, а затем выполняется код в каждом конструкторе всех производных классов; код в производном классе самого дальнего уровня выполняется последним.

Screenshot showing class hierarchy constructors and inheritance.

Когда объект больше не нужен, среда CLR вызывает метод Finalize для этого объекта перед высвобождением памяти. Метод Finalize называется destructor, поскольку он выполняет задачи очистки, такие как сохранение информации о состоянии, закрытие файлов и подключений к базам данных, а также прочие задачи, которые необходимо выполнить перед высвобождением объекта.

Screenshot showing the Finalize method destructor.

Интерфейс IDisposable

Экземпляры классов зачастую управляют ресурсами, которыми не управляет среда CLR, такими как дескрипторы Windows и подключения к базам данных. От этих ресурсов нужно избавляться в методе Finalize класса, поэтому они будут высвобождаться при уничтожении объекта сборщиком мусора. Тем не менее, сборщик мусора уничтожает объекты только в тех случаях, когда среде CLR нужно больше свободной памяти. Это означает, что ресурсы могут быть не высвобождены еще в течение долгого времени после того, как объект выйдет из области.

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

Использование IDisposable

Класс, реализующий интерфейс IDisposable, должен включать следующие разделы кода:

  • Поле, чтобы отслеживать, уничтожен ли объект:

    Protected disposed As Boolean = False
    
  • Перегрузка Dispose для высвобождения ресурсов класса. Этот метод должен вызываться методами Dispose и Finalize базового класса:

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposed Then
            If disposing Then
                ' Insert code to free managed resources.
            End If
            ' Insert code to free unmanaged resources.
        End If
        Me.disposed = True
    End Sub
    
  • Реализация Dispose, содержащая только следующий код:

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
  • Переопределение метода Finalize, содержащее только следующий код:

    Protected Overrides Sub Finalize()
        Dispose(False)
        MyBase.Finalize()
    End Sub
    

Производное создание от класса, реализующего IDisposable

Классу, производному от базового класса, реализующего интерфейс IDisposable, нет необходимости переопределять какие-либо базовые методы, если только производный класс не использует дополнительные ресурсы, которые следует высвобождать. В этом случае производный класс должен переопределять метод Dispose(disposing) базового класса, чтобы удалить ресурсы производного класса. Это переопределение должно вызвать метод Dispose(disposing) базового класса.

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposed Then
        If disposing Then
            ' Insert code to free managed resources.
        End If
        ' Insert code to free unmanaged resources.
    End If
    MyBase.Dispose(disposing)
End Sub

Производный класс не должен переопределять методы Dispose и Finalize базового класса. Когда эти методы вызываются из экземпляра производного класса, реализация этих методов в базовом классе вызывает переопределение метода Dispose(disposing) производного класса.

Сбор мусора и деструктор Finalize

Платформа .NET Framework использует систему сборки мусора для трассировки ссылок для периодического выпуска неиспользуемых ресурсов. Visual Basic 6.0 и более ранних версий использовали другую систему, называемую подсчетом ссылок для управления ресурсами. Обе системы автоматически выполняют одну и ту же функцию, но есть несколько важных различий.

CLR периодически уничтожает объекты, если система определяет, что эти объекты больше не нужны. Объекты высвобождаются быстрее при нехватке системных ресурсов и медленнее в других случаях. Задержка между потерей объектом области и высвобождением объекта средой CLR означает, что в отличие от объектов в Visual Basic 6.0 и более ранних версиях, невозможно точно определить, когда объект будет уничтожен. В такой ситуации объекты, как говорят, имеют недетерминированное время существования. В большинстве случаев неопределенное время жизни не влияет на написание приложений, если помнить о том, что деструктор Finalize может быть выполнен не сразу после потери объектом области.

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

См. также