Tempo de vida do objeto: como os objetos são criados e destruídos (Visual Basic)

Uma instância de uma classe, um objeto, é criada usando a palavra-chave New . As tarefas de inicialização geralmente devem ser executadas em novos objetos antes de serem usadas. As tarefas comuns de inicialização incluem abrir arquivos, conectar-se a bancos de dados e ler valores de chaves do Registro. Visual Basic controla a inicialização de novos objetos usando procedimentos chamados construtores (métodos especiais que permitem o controle sobre a inicialização).

Depois que um objeto deixa o escopo, ele é liberado pelo Common Language Runtime (CLR). Visual Basic controla a liberação de recursos do sistema usando procedimentos chamados destruidores. Juntos, construtores e destruidores suportam a criação de bibliotecas de classes robustas e previsíveis.

Usando construtores e destruidores

Construtores e destruidores controlam a criação e destruição de objetos. Os Sub New procedimentos e Sub Finalize no Visual Basic inicializam e destroem objetos, eles substituem os Class_Initialize métodos e Class_Terminate usados no Visual Basic 6.0 e versões anteriores.

Sub Novo

O Sub New construtor pode executar apenas uma vez quando uma classe é criada. Ele não pode ser chamado explicitamente em qualquer outro lugar que não seja na primeira linha de código de outro construtor da mesma classe ou de uma classe derivada. Além disso, o código no método sempre é executado antes de Sub New qualquer outro código em uma classe. Visual Basic implicitamente cria um Sub New construtor em tempo de execução se você não definir explicitamente um Sub New procedimento para uma classe.

Para criar um construtor para uma classe, crie um procedimento nomeado Sub New em qualquer lugar na definição de classe. Para criar um construtor parametrizado, especifique os nomes e tipos de dados de argumentos da Sub New mesma forma que você especificaria argumentos para qualquer outro procedimento, como no código a seguir:

Sub New(ByVal s As String)

Os construtores são frequentemente sobrecarregados, como no código a seguir:

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

Quando você define uma classe derivada de outra classe, a primeira linha de um construtor deve ser uma chamada para o construtor da classe base, a menos que a classe base tenha um construtor acessível que não usa parâmetros. Uma chamada para a classe base que contém o construtor acima, por exemplo, seria MyBase.New(s). Caso contrário, MyBase.New é opcional e o tempo de execução do Visual Basic o chama implicitamente.

Depois de escrever o código para chamar o construtor do objeto pai, você pode adicionar qualquer código de inicialização adicional ao Sub New procedimento. Sub New pode aceitar argumentos quando chamado como um construtor parametrizado. Esses parâmetros são passados do procedimento que chama o construtor, por exemplo, Dim AnObject As New ThisClass(X).

Sub Finalizar

Antes de liberar objetos, o CLR chama automaticamente o Finalize método para objetos que definem um Sub Finalize procedimento. O Finalize método pode conter código que precisa ser executado imediatamente antes de um objeto ser destruído, como código para fechar arquivos e salvar informações de estado. Há uma pequena penalidade de desempenho para a execução Sub Finalize, portanto, você deve definir um Sub Finalize método somente quando precisar liberar objetos explicitamente.

Nota

O coletor de lixo no CLR não descarta (e não pode) objetos não gerenciados, objetos que o sistema operacional executa diretamente, fora do ambiente CLR. Isso ocorre porque diferentes objetos não gerenciados devem ser descartados de maneiras diferentes. Essas informações não estão diretamente associadas ao objeto não gerenciado; ele deve ser encontrado na documentação para o objeto. Uma classe que usa objetos não gerenciados deve descartá-los em seu Finalize método.

O Finalize destruidor é um método protegido que pode ser chamado apenas da classe a que pertence ou de classes derivadas. O sistema chama Finalize automaticamente quando um objeto é destruído, portanto, você não deve chamar Finalize explicitamente de fora da implementação de uma classe Finalize derivada.

Ao contrário Class_Terminatedo , que é executado assim que um objeto é definido como nada, geralmente há um atraso entre quando um objeto perde o escopo e quando o Visual Basic chama o Finalize destruidor. Visual Basic .NET permite um segundo tipo de destruidor, IDisposable.Dispose, que pode ser explicitamente chamado a qualquer momento para liberar recursos imediatamente.

Nota

Um Finalize destruidor não deve lançar exceções, porque elas não podem ser manipuladas pelo aplicativo e podem fazer com que o aplicativo seja encerrado.

Como novos métodos e finalizar funcionam em uma hierarquia de classe

Sempre que uma instância de uma classe é criada, o Common Language Runtime (CLR) tenta executar um procedimento chamado New, se ele existir nesse objeto. New é um tipo de procedimento chamado a constructor que é usado para inicializar novos objetos antes que qualquer outro código em um objeto seja executado. Um New construtor pode ser usado para abrir arquivos, conectar-se a bancos de dados, inicializar variáveis e cuidar de quaisquer outras tarefas que precisem ser feitas antes que um objeto possa ser usado.

Quando uma instância de uma classe derivada é criada, o Sub New construtor da classe base é executado primeiro, seguido por construtores em classes derivadas. Isso acontece porque a primeira linha de código em um Sub New construtor usa a sintaxe MyBase.New()para chamar o construtor da classe imediatamente acima de si mesmo na hierarquia de classe. O Sub New construtor é então chamado para cada classe na hierarquia de classe até que o construtor para a classe base seja alcançado. Nesse ponto, o código no construtor para a classe base é executado, seguido pelo código em cada construtor em todas as classes derivadas e o código nas classes mais derivadas é executado por último.

Screenshot showing class hierarchy constructors and inheritance.

Quando um objeto não é mais necessário, o CLR chama o Finalize método para esse objeto antes de liberar sua memória. O Finalize método é chamado de porque destructor executa tarefas de limpeza, como salvar informações de estado, fechar arquivos e conexões com bancos de dados e outras tarefas que devem ser feitas antes de liberar o objeto.

Screenshot showing the Finalize method destructor.

IDisposable Interface

As instâncias de classe geralmente controlam recursos não gerenciados pelo CLR, como identificadores do Windows e conexões de banco de dados. Esses recursos devem ser descartados no Finalize método da classe, para que sejam liberados quando o objeto for destruído pelo coletor de lixo. No entanto, o coletor de lixo destrói objetos somente quando o CLR requer mais memória livre. Isso significa que os recursos podem não ser liberados até muito depois que o objeto sair do escopo.

Para complementar a coleta de lixo, suas classes podem fornecer um mecanismo para gerenciar ativamente os recursos do sistema se implementarem a IDisposable interface. IDisposable tem um método, Dispose, que os clientes devem chamar quando terminarem de usar um objeto. Você pode usar o Dispose método para liberar recursos imediatamente e executar tarefas como fechar arquivos e conexões de banco de dados. Ao contrário do Finalize destruidor, o Dispose método não é chamado automaticamente. Os clientes de uma classe devem chamar Dispose explicitamente quando você quiser liberar recursos imediatamente.

Implementando IDisposable

Uma classe que implementa a IDisposable interface deve incluir estas seções de código:

  • Um campo para controlar se o objeto foi descartado:

    Protected disposed As Boolean = False
    
  • Uma sobrecarga do Dispose que liberta os recursos da turma. Este método deve ser chamado pelos Dispose métodos e Finalize da classe base:

    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
    
  • Uma implementação disso Dispose contém apenas o seguinte código:

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
  • Uma substituição do Finalize método que contém apenas o seguinte código:

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

Derivando de uma classe que implementa IDisposable

Uma classe que deriva de uma classe base que implementa a IDisposable interface não precisa substituir nenhum dos métodos base, a menos que use recursos adicionais que precisam ser descartados. Nessa situação, a classe derivada deve substituir o método da Dispose(disposing) classe base para dispor dos recursos da classe derivada. Essa substituição deve chamar o método da Dispose(disposing) classe base.

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

Uma classe derivada Dispose não deve substituir a classe base e Finalize os métodos. Quando esses métodos são chamados a partir de uma instância da classe derivada, a implementação desses métodos pela classe base chama a substituição do Dispose(disposing) método pela classe derivada.

Coleta de lixo e o destruidor finalizado

O .NET Framework usa o sistema de coleta de lixo de rastreamento de referência para liberar periodicamente recursos não utilizados. Visual Basic 6.0 e versões anteriores usavam um sistema diferente chamado contagem de referência para gerenciar recursos. Embora ambos os sistemas executem a mesma função automaticamente, existem algumas diferenças importantes.

O CLR destrói periodicamente objetos quando o sistema determina que esses objetos não são mais necessários. Os objetos são liberados mais rapidamente quando os recursos do sistema estão em falta e, com menos frequência, caso contrário. O atraso entre quando um objeto perde o escopo e quando o CLR é liberado significa que, ao contrário dos objetos no Visual Basic 6.0 e versões anteriores, você não pode determinar exatamente quando o objeto será destruído. Em tal situação, diz-se que os objetos têm uma vida útil não determinística. Na maioria dos casos, o tempo de vida não determinístico não altera a forma como você escreve aplicativos, desde que você se lembre de que o Finalize destruidor pode não ser executado imediatamente quando um objeto perde o escopo.

Outra diferença entre os sistemas de coleta de lixo envolve o uso de Nothing. Para aproveitar a contagem de referência no Visual Basic 6.0 e versões anteriores, os programadores às vezes atribuíam Nothing variáveis de objeto para liberar as referências que essas variáveis mantinham. Se a variável contivesse a última referência ao objeto, os recursos do objeto eram liberados imediatamente. Em versões posteriores do Visual Basic, embora possa haver casos em que esse procedimento ainda é valioso, executá-lo nunca faz com que o objeto referenciado libere seus recursos imediatamente. Para liberar recursos imediatamente, use o método do Dispose objeto, se disponível. O único momento em que você deve definir uma variável é Nothing quando sua vida útil é longa em relação ao tempo que o coletor de lixo leva para detetar objetos órfãos.

Consulte também