O Poder das Classes no Visual Basic

A partir da versão 4 do Visual Basic, o programador passou a poder criar seus próprios objetos. Um objeto é uma instância de uma classe assim como Siegmund é uma instância do tipo DoninhaTFB. Uma classe é um tipo de dado que o usuário cria contendo tudo o que já vimos sobre tipos de dados definidos pelo usuário e muito mais. Além de variáveis uma classe tem um comportamento. Isto quer dizer que dentro de uma classe você não apenas cria variáveis para armazenar dados, mas também pode criar procedimentos que serão executados quando chamados. Estes procedimentos são chamados métodos da classe. Um método pode ser público ou não. Se for público, poderá ser chamado de qualquer ponto do programa. Caso seja privativo da classe, somente o código de um outro método da própria classe poderá comandar a sua execução.

Esta mesma distinção entre público e privado se aplica às variáveis contidas na classe. Essas variáveis são chamadas variáveis membros da classe. Uma variável membro também pode ser privativa à classe e somente poder ser acessada por métodos da classe, ou ser pública e ter seu conteúdo livremente acessado de qualquer ponto do programa. A esta característica de proteção que a classe pode dar ao seu conteúdo se dá o nome de encapsulamento. O encapsulamento, porém, não é uma barreira definitiva para o acesso ao conteúdo das variáveis privativas da classe. Isto porque uma variável privada pode ser acessada indiretamente pelo mundo exterior à classe. O modo como isto é feito é usando um método público da classe que nos permita ler ou alterar o valor da variável. Agora você deve estar se perguntando: mas se é para tornar uma variável passível de ser alterada por partes do programa externas à classe, porque então não torná-la pública de um vez? A resposta para isto é que muitas vezes você quer ter controle sobre a faixa dos valores que serão atribuídos a uma variável e também o tipo de dado que se pode atribuir a ela. No método que se usa para acessar a variável, esta verificação pode ser feita antes que algo de errado seja atribuído ela.

Ao procedimento que nos dá acesso à variável damos o nome de propriedade. Externamente à classe uma propriedade é vista como um único nome, mas que internamente à classe representa dois ou três procedimentos diferentes. Dependendo do papel que o nome da propriedade desempenha numa expressão, um dos procedimentos é chamado. Se a propriedade está sendo referida para se obter o seu valor, é chamado o procedimento Get_NomeDaPropriedade. Se a propriedade está sendo referida para se atribuir um valor a ela, é chamado o procedimento Let_NomeDaPropriedade (para tipos de dados comuns) ou Set_NomeDaPropriedade (para objetos).

Outra vantagem que as classes oferecem é chamada de polimorfismo. Por polimorfismo entende-se a possibilidade de atribuir-se um mesmo nome a diferentes procedimentos. Uma classe pode ter um procedimento (um método) com o nome Calcular, que signifique fazer o cálculo de uma área. Enquanto isso uma outra classe pode ter um método com o mesmo nome para calcular o total de uma lista de compras.

Vejamos agora com mais detalhamento como trabalhar com classes.

Criando suas Próprias Classes e Objetos

Além das classes e objetos que o Visual Basic e outras aplicações lhe oferecem, você pode criar suas próprias classes e usá-las para criar objetos em tempo de execução dos seus programas.

Você inicia a criação de uma classe pela adição de um módulo de classe ao seu projeto. Abrindo o menu Project e selecionando Add Class Module, uma caixa de diálogo surgirá dando lhe três opções para a criação do módulo de classe. Uma, Class Module, lhe permite adicionar um módulo de classe vazio para ser completado por você outra opção, Vb Class Builder, lhe dá acesso a um assistente de criação de classes que lhe poupa parte do trabalho de codificação da nova classe; a terceira opção refere-se à criação de Add-Ins, um tema complexo que vai além do propósito desta discussão. A segunda opção é sem dúvida a mais prática, mas antes que você a utilize é interessante conhecer passo-a-passo os detalhes da criação de uma classe seguindo o caminho mais trabalhoso. Vamos supor aqui que você tenha iniciado o procedimento de criação de uma classe e feito a opção por criar um módulo de classe vazio. Nos tópicos seguintes expomos o que constitui uma classe e o que fazer para criar suas classes.

Como é Constituída uma Classe?

Uma classe pode possuir propriedades, métodos e eventos. Em geral, propriedades representam informações sobre um objeto, enquanto métodos representam ações que um objeto pode realizar. Em outras palavras, as propriedades descrevem o objeto e os métodos são o seu comportamento. Quanto aos eventos, estes são procedimentos que um objeto chama dentro de um outro objeto que o contém.

Propriedades

O modo mais simples de adicionar uma propriedade a uma classe é criando uma variável dentro da classe e declarando-a como pública. Exemplo: Public variável as string. Desta forma poderemos atribuir valores à variável em qualquer parte do código da aplicação. Mas este não é o modo mais correto e usual de se criar uma propriedade. Costumamos querer que as propriedades de nossos objetos sejam acessadas de modo controlado pelo próprio objeto. Isto é, queremos poder definir, por exemplo, que uma propriedade é somente para leitura ou ter a garantia de que os valores atribuídos a ela se situam dentro de uma determinada faixa. A forma que usamos para conseguir isto e a criação dos procedimentos de propriedade. Procedimentos de propriedade são procedimentos usados para se ter acesso ao valor de uma variável interna à classe usada para guardar o valor de uma propriedade. Os procedimentos de propriedade se dividem em dois tipos: procedimentos de leitura do valor da propriedade e procedimentos de atribuição de valor à propriedade. Nos procedimentos de leitura inserimos o código que retorna o valor da variável usada para guardar o valor da propriedade no interior da classe. Nos procedimentos de atribuição de valor inserimos o código responsável pela atribuição de valor e - se for o caso - validação do valor que se está tentando atribuir à propriedade. Caso queiramos que uma propriedade seja apenas de leitura, não criaremos procedimentos de atribuição de valores a ela. Qualquer tentativa de atribuir valor a uma propriedade que não tenha uma procedimento para atribuição gerará uma erro.

A criação dos procedimentos de propriedades seguem determinadas regras. Os procedimentos usados para retornar o valor da propriedade têm o seu nome composto pela palavra Get seguida de um caracter de sublinhado ( _ ) e pelo nome da propriedade. Estes procedimentos retornam um valor do tipo de dados da variável que armazena o valor da propriedade. Os procedimentos de atribuição têm seus nomes formados pela palavra Let ou Set seguida de um caractere de sublinhado ( _ ) e do nome da propriedade. Estes procedimentos recebem como argumento o novo valor que se quer atribuir à propriedade. A seguir exibimos a sintaxe de criação desses procedimentos.

Public | Private Get_NomePropriedade () As Tipo

Código usado para retornar o valor da propriedade.

Ex.: NomePropriedade = mvarDataCriação, onde mvarDataCriação

é a variável que armazena o valor da propriedade no interior da classe.

Isto faz com que o procedimento retorne este valor.

End Property

Public | Private Let_NomePropriedade ( novovalor As Tipo )

Código usado para armazenar o valor da propriedade.

Ex.: mvarDataCriação = novovalor, onde mvarDataCriação

é a variável que armazena o valor da propriedade no interior da classe e

novovalor é o valor que se quer atribuir à propriedade recebido como

argumento pelo procedimento de atribuição.

End Property

Public | Private Set_NomePropriedade ( novovalor As Tipo )

Código usado para armazenar o valor da propriedade.

Ex.: Set mvarDataCriação = novovalor, onde mvarDataCriação

é a variável que armazena o valor da propriedade no interior da classe e

novovalor é o valor que se quer atribuir à propriedade.

End Property

Você pode estar se perguntando sobre o porquê de termos dois tipos diferentes de procedimentos de atribuição de valores às propriedades, uns com nome iniciando por Let e outro por Set. A razão para isto é que ocorre às vezes de uma propriedade armazenar valores que são referências a objetos. Nestes casos o procedimento usado para atribuir valores à propriedade tem seu nome iniciado por Set e no seu código devemos usar a instrução Set para atribuir o valor à variável da propriedade. Variáveis usadas para armazenar referências a objetos não têm seus valores atribuídos pelo uso simples do sinal de atribuição ( = ), mas carecem também que se use a instrução Set antes do seu nome. Exemplo: Set varForm = Form1; esta instrução armazena uma referência ao objeto Form1 na variável varForm.

Há casos, no entanto, em que a propriedade pode armazenar tanto referências a objetos como a tipos de dados comuns. Caso a variável interna tenha sido declarada como sendo do tipo Variant, poderemos atribuir qualquer tipo de dado à propriedade. Nestes casos nos utilizamos da técnica de usar apenas o procedimento Let e declaramos o tipo de dado do seu argumento como Variant. Antes de fazermos a atribuição de valor à propriedade, verificamos o tipo do dado que está sendo passado e optamos pela forma correta de fazermos esta atribuição. Veja abaixo como isto é feito:

Public Let_NomePropriedade ( novovalor as Variant )

If IsObject(novovalor) Then ‘função IsObject retorna True se novovalor é objeto

Set varPropriedade = novovalor

Else

VarPropriedade = novovalor

End If

End Property

Até aqui vimos como criamos os procedimentos de propriedade dentro da classe, mas fora da classe estes procedimentos não são utilizados com os mesmos nomes que usamos para criá-los na classe. Para atribuirmos valor a uma propriedade ou termos como retorno o seu valor atual, utilizamos apenas o nome da propriedade. Baseado na papel que o nome da propriedade esteja desempenhando numa expressão é que o Visual Basic vai decidir qual procedimento da propriedade chamar. Se o nome da propriedade estiver à esquerda de um sinal de atribuição, o Visual Basic sabe que tem que chamar um procedimento usado para armazenar o valor da propriedade ( nome iniciado por Let_ ou Set_ ). Se o nome da propriedade estiver à direita de um operador de atribuição ou em qualquer outra situação em que se espera um valor de retorno, o Visual Basic chamará o procedimento da propriedade usado para retornar seu valor ( nome iniciado por Get_ ). Exemplo:

Data = Arquivo1.DataCriação

‘É chamado o procedimento Get_DataCriação, que retorna o valor da ‘propriedade DataCriação do objeto Arquivo1.

Arquivo1.DataCriação = Now

‘É chamado o procedimento Let_DataCriação para atribuir a data

‘retornada pela função Now à propriedade DataCriação do objeto Arquivo1.

Métodos

Os métodos nada mais são do que os procedimentos e funções públicos de uma classe. Para chamar um método de fora do código da classe, basta usar o nome do objeto seguido de um ponto e do nome do método. Exemplo: Form1.Show; esta instrução faz com que o método Show do objeto Form1 seja executado.

Os métodos de uma classe têm acesso às variáveis privadas da classe.

Eventos

Como já foi dito acima, eventos são procedimentos chamados por um objeto em um outro objeto que o contém como resposta a um acontecimento que lhe ocorre. Os eventos podem ser disparados pelo próprio objeto em alguma parte do seu código utilizando-se da instrução RaiseEvent. Pode-se também utilizar o artifício de criar um método só para disparar um evento dentro do objeto. Um método assim seria chamado de fora do objeto para disparar o evento no seu interior.

Para definir um evento dentro de uma classe não se cria um procedimento de evento dentro do módulo da classe. O que se faz é usar a instrução Event para atribuir um nome ao evento e definir sua lista de argumentos, ou seja, as informações que serão passadas ao procedimento de tratamento do evento. Este procedimento sim será codificado dentro do objeto que contenha o objeto disparador do evento caso se queira tratar o evento. Veja como é a sintaxe da instrução Event:

Public Event NomeEvento ( [ByVal] argumento As Tipo [, ...] )

Após ter sido definido o evento na classe, ele poderá ser tratado por um procedimento de evento dentro de um módulo que contenha uma declaração de variável do tipo da classe disparadora do evento, e que tenha sido declarada com a palavra-chave WithEvents. Vejamos um exemplo de como isto funciona.

Crie um novo projeto e adicione um módulo de classe. Selecione a propriedade Name do módulo de classe na janela de propriedades e atribua CMensagem como novo nome para a classe. No módulo da classe, adicione as seguintes linhas de código:

‘Definição do evento chamado Mensagem

Public Event Mensagem ( texto As String )

‘Método público para disparar o evento Mensagem de fora da classe

Public Sub DispararEvento ( argumento As String )

RaiseEvent Mensagem (argumento )

End Sub

Agora, entre na janela de código do formulário Form1. Adicione as seguintes linhas de código na seção de declarações gerais do módulo de Form1:

Private WithEvents Msg As CMensagem

Examine a relação de objetos que aparecem na caixa de objetos da janela de código. Verifique que além do objeto Form1, também aparece o objeto Msg. Se você selecioná-lo, verá na caixa de procedimentos o nome do evento Mensagem. Este é o resultado de termos acrescentado a palavra chave WithEvents na declaração da variável Msg. WithEvents é usada para informar que a variável será usada para armazenar uma referência a um objeto que possui eventos, e que queremos tratar estes eventos dentro do módulo. Assim como você está acostumado a ver a relação de todos os procedimentos de evento para os controles adicionados ao formulário, você vê agora os eventos para o objeto Msg na lista de procedimentos de evento. Do mesmo modo como escreve código para tratar os eventos dos controles, você pode fazê-lo para tratar o evento Mensagem do objeto Msg.

Dê um duplo clique sobre o evento Mensagem na lista para que apareça o seu procedimento. Agora escreva o seguinte código dentro do procedimento do evento:

MsgBox texto

No procedimento Form1_Load acrescente:

‘Cria uma nova instância da classe CMensagem e

‘armazena uma referência a ela em Msg.

Set Msg = New Cmensagem

‘Dispara o evento Mensagem em Msg

Msg.DispararEvento "Isto é uma mensagem!"

Pronto. Agora pressione F5 para executar.

Tão logo o formulário Form1 inicie a sua carga, você verá a mensagem "Isto é uma mensagem!" como resultado da chamada ao método DispararEvento que ativa o evento Mensagem no objeto Msg.

Numa outra situação, você poderia desejar que um objeto container de um objeto disparador de eventos fosse informado todas as vezes que uma determinada propriedade do objeto contido sofresse alteração. Seria o caso então de se criar um evento para isso e dispara-lo com RaiseEvent dentro do procedimento de atribuição de valor da propriedade.

Eventos Comuns a Todas as Classes

Além dos eventos que você define para suas classes, elas têm em comum com todas as demais dois eventos particularmente interessantes: initialize e terminate. Estes eventos são disparados quando da criação de um objeto da classe e quando da sua destruição respectivamente. O tratamento destes eventos, ao contrário dos eventos que você define, ocorre no interior do módulo da classe. Para ver o arcabouço destes procedimentos entre no módulo da classe CMensagem criada no exemplo acima e selecione Class na caixa de objetos da janela de código. Ao fazer esta seleção, examine a caixa de procedimentos e veja que os nomes dos eventos Initialize e Terminate estão relacionados. Caso queira executar algum código de inicialização ou de encerramento para os objetos da classe, é nos procedimentos de tratamento destes eventos que tal código deve ser adicionado.

Criando e Destruindo Objetos

Já vimos no exemplo da classe CMensagem o uso da palavra New para criar uma instância de um objeto da classe. Ali, o uso de New foi feito fora da declaração da variável porque não é permitido declarar uma variável com WithEvents e na mesma instrução de declaração utilizar a palavra New. Mas, na ausência de WithEvents, você pode declarar uma variável e ao mesmo tempo usar New para que, na primeira vez que a variável for usada no código, um objeto da classe da variável seja criado e a variável passe a conter uma referência a este objeto. Veja um exemplo:

Private Msg As New CMensagem ' Declaração da variável

Msg.DispararEvento "Já sou um objeto!"

 

No exemplo acima nenhuma mensagem seria produzida, porque nenhum procedimento de evento poderia tratar o evento disparado - já que a variável não foi declarada usando WithEvents -, mas ilustra a não necessidade de se usar a instrução Set Msg = New CMensagem para criar um objeto da classe quando se usa New já na declaração.

O processo de criação de objetos a partir das suas classes é mais simples do que o processo de destruição desses objetos. Um objeto só pode ser eliminado da memória quando não há mais nenhuma referência a este objeto em qualquer variável. Há duas formas de as variáveis deixarem de referenciar um objeto: uma é quando são destruídas por saírem do escopo do código que está sendo executado; outra é quando lhes é atribuído um novo valor. Os programadores costumam atribuir o valor Nothing a toda variável de objeto que não precise mais referenciar um objeto. Desta forma o objeto libera a memória tão logo deixe de ser necessário para a aplicação. Exemplo:

Set varObjeto = Nothing

Há uma observação, no entanto, a ser feita sobre isso no que diz respeito aos objetos criados com a palavra New na declaração da variável: tais objetos não deixam de existir quando se atribui Nothing às variáveis que os contêm. A explicação para isto vem do fato de que ao usar New numa declaração de variável, o compilador faz com que uma instancia da classe (um objeto) seja criado e atribuído à variável no caso de ela já não estar armazenando referência a um objeto. Quando você atribui Nothing à variável, está limpando-a de qualquer referência a um objeto, mas também está referenciando-a dentro do seu código. Isto produz uma nova instanciação de objeto e nova atribuição à variável. Evite, portanto, o uso da palavra New nas declarações de variáveis. Declare as variáveis de objeto de modo simples e depois atribua um valor de objeto usando Set variável = New ClasseDoObjeto em alguma parte do código.

Referências Circulares entre Objetos

Há um problema que pode passar desapercebido para os programadores iniciantes no uso de objetos que é o das referências circulares. Referências circulares ocorrem quando um objeto tem uma variável membro usada para armazenar uma referência a outro objeto, e este outro objeto por sua vez contém uma variável que faz referência ao primeiro objeto. Nestes casos um objeto não pode ser destruído porque há uma referência a ele dentro do outro e vice-versa. A solução para isto é atribuir Nothing às variáveis membros de cada um para só então atribuir Nothing às variáveis externas aos objetos que guardam referências a eles.

 

RabJump facilita a depuração do seu código