## Capítulo 3. Principais Mudanças do VB .NET NESTE CAPÍTULO ### Mudanças Gerais ### Novos Itens O VB. NET apresenta grandes mudanças para a linguagem Visual Basic. Algumas são modificações a formas já existentes de trabalhar, enquanto outras são novas por inteiro. Este capítulo irá cobrir algumas destas mudanças, mas não é uma lista exaustiva de todas as mudanças do VB para o VB .NET. Inicialmente você verá alguns dos recursos que foram modificados. Então, você irá ver alguns dos recursos inteiramente novos. ### Mudanças Gerais Há um número de mudanças gerais para se conhecer na migração do VB para o VB .NET. Entre elas há tópicos tais como a remoção das propriedades default, subs e funções requerendo parênteses e ByVal passando a ser o modo padrão de passagem de parâmetros. Estas mudanças e outras serão detalhadas nesta seção. ### Propriedades Padrão No VB6, objetos poderiam ter propriedades padrão. Por exemplo, o código seguinte é perfeitamente válido no VB6, se você assumir que Text1 é uma textbox: Text1="Hello, World" Este código atribui a string "Hello Word" à propriedade default da textbox, a propriedade Text. A principal desvantagem em ter propriedades default é que elas exigem que você tenha um comando Set no VB. Por exemplo, dê uma olhada no seguinte bloco de código em VB6:: Dim txtBillTo as TextBox Dim txtShipTo as TextBox txtShipTo = txtBillTo A linha de código txtShipTo = txtBillTo atribui o valor da propriedade Text de txtShipTo ao valor da propriedade Text de txtBillTo. Mas, e se isto não fosse o que você desejasse? E se, ao contrário, você quisesse criar uma referência em txtShipTo que referenciasse o objeto txtBillTo? Você teria que usar este código: Set txtShipTo = txtBillTo Como você pode ver, propriedades default exigem que você use Set para atribuir referências de uma variável de objeto a outra. O VB .NET se livra deste problema livrando-se das propriedades default. Portanto, para copiar a propriedade Text de txtBillTo para a propriedade Text de txtShipTo, você tem de usar este código: txtShipTo.Text = txtBillTo.Text Atribuindo uma variável à outra, faz com que a referência de uma seja passada para a outra. Em outras palavras, você obtém uma referência a um objeto sem usar a palavra Set. txtShipTo = txtBillTo ' Referência de objeto no VB.NET ### Propriedades Default Requerem Propriedades Parametrizadas Para ser mais preciso, as propriedades default sem parâmetros não mais são suportadas. Propriedades default que requerem parâmetros são ainda suportadas. Propriedades default com parâmetros são muito comuns em classes de coleções, como em ADO. Num exemplo de ADO, se você assumir que rs é um recordset ADO, confira o seguinte código: rs.Fields.Item(x).Value ' OK, completamente qualificado rs.Fields(x).Value ' OK, porque o item é parametrizado rs.Fields(x) ' Erro, porque Fields não é parametrizado A solução simples é qualificar totalmente tudo. Isto evita qualquer confusão sobre quais propriedades são parametrizadas e quais não são. Contudo, como você sabe dos seus dias de VB, o número de pontos que você usa devem ser minimizados. A razão para isto é que cada ponto requer uma pesquisa de OLE, o que torno o código mais lento. Portanto, você deve codificar cuidadosamente quando estiver lidando com parâmetros. ### Subs e Funções Requerem Parênteses Como você viu no último capítulo, quando você usa a função MsgBox, você precisa sempre usar parênteses com funções, mesmo se você estiver ignorando o valor de retorno. Em adição, você precisa usar parênteses também quando chamar subs, o que você não precisava fazer no VB6. Por exemplo, assuma que você tem este sub tanto em VB6 quanto em VB .NET: Sub foo(ByVal Greeting As String) 'código de implementação aqui End Sub No VB6, você poderia chamar este sub de duas formas: foo "Hello" Call foo("Hello") No VB .NET, você também pode chamar este sub de duas formas: Foo("Hello") Call foo("Hello") A diferença entre o modo como o VB e o VB .NET tratam isto é, naturalmente, que o VB .NET sempre requer os parênteses nas chamadas, mesmo que você não esteja retornando nada. No VB .NET, o comando Call ainda é suportado, mas não é realmente necessário. ### Mudanças em Operadores Booleanos Os operadores And, Not e Or eram para ter sofrido algumas mudanças no VB .NET. Inicialmente a Microsoft disse que estes operadores iriam ter um curto-circuitar, mas no momento eles continuam funcionando do mesmo modo que eram no VB6. Curto-circuitar significa dizer que, se você estivesse examinando duas condições com um operador AND e a primeira delas fosse falsa, seu programa não precisaria examinar a segunda condição. Seu código ficaria mais eficiente. Mas, como não houve nenhuma mudança no VB .NET, isto significa que, assim como no VB6, se você tiver duas condições em um AND e a primeira falhar, você ainda terá que examinar a segunda. Examine o seguinte código: Dim x As Integer Dim y As Integer x = 1 y = 0 If x = 2 And y = 5/y Then ... Como um humano, você sabe que a variável x é igual a 1. Portanto, quando você examina a primeira parte do If, você sabe que x não é igual a 2, assim você pensaria logicamente que deveria parar de avaliar a expressão. Contudo, o VB e o VB .NET examinam a segunda parte da expressão, assim este código irá causar um erro de divisão por zero, mesmo que o erro esteja na segunda parte da expressão e a primeira parte já tenha sido reconhecida como falsa. Se você quer curto-circuitar, o VB .NET apresenta agora uma dupla de novos operadores: AndAlso e OrElse. Neste caso, o código seguinte não gerará um erro no VB .NET: Dim x As Integer Dim y As Integer x = 1 y = 0 If x = 2 AndAlso y = 5/y Then ... Este código não causa um erro; por x não ser igual a 2, o VB .NET não examina a segunda condição. ### Mudanças em Declarações Você pode agora inicializar suas variáveis quando você as declara. Você não podia fazer isto no VB6. No VB6, a única forma de inicializar variáveis era fazendo-o numa linha separada, como esta: Dim x As Integer x = 5 No Vb .NET, você pode reescrever isto em uma só linha: Dim x As Integer = 5 Uma outra mudança significativa, e muito solicitada, é a de poder declarar múltiplas variáveis e o tipo de dados a ser assumido por todas elas de uma só vez. Exemplo: Dim x, y As Integer Na declaração acima, como você provavelmente sabe, no VB6, y será um tipo de dados Integer, e x um Variant. No VB .NET, isto mudou, assim ambos x e y são Integers. Se você pensar, "Já era em tempo", há muitos que concordam com você. Isto remove muitos bugs e estranhas conversões de tipos que novos programadores experimentam. Fazer com que as variáveis sejam de um tipo esperado também faz com que o código fique mais eficiente. ### Suporte para Novos Operadores de Atribuição O VB .NET agora suporta atalhos para fazer certas operações de atribuição. No VB6, você incrementava x de 1 com a seguinte linha de código: x = x + 1 No VB .NET, você pode criar um comando equivalente escrevendo assim: x += 1 Não apenas você pode usar o sinal de mais, mas o VB .NET agora suporta -=, *=, /=, \= e ^= para operações matemáticas e &= para concatenação de strings. Se tudo isto se parece com C/C++, é de lá que veio. Contudo o operador ++ não é suportado. Os desenvolvedores da Microsoft tomaram a decisão de não incluir o operador ++ por achar que isto faria o código menos legível. ByVal Agora é o Default O que muitos consideram uma estranha decisão, o modo padrão de passagem de parâmetros no VB tem sido sempre a passagem por referência (ByRef). A escolha foi feita porque realmente a passagem por referência é mais rápida quando é feita dentro da mesma aplicação, mas pode se tornar ineficiente quando se faz chamadas a componentes rodando em outro processo. Se você não sabe bem o que passagem por referência, por referência significa que você está passando apenas um endereço de memória de uma variável dentro da rotina chamadora. Se a rotina chamada modifica a variável, ela realmente atualiza a variável no endereço de memória passado, fazendo com que a variável se modifique também na rotina que a chamou. Passando dados por valor (ByVal) significa que você está passando uma cópia dos dados à rotina chamada. A rotina chamada precisa reservar uma nova área na memória para armazenar aquele valor. Se a rotina chamada modifica o valor, ele é modificado no endereço de memória reservado pela rotina chamada, mas a localização de memória da rotina chamadora fica intacta. Veja este exemplo em VB6: Private Sub Command1_Click() Dim x As Integer x = 3 foo x MsgBox x End Sub Sub foo(y As Integer) y = 5 End Sub Se você rodar este exemplo no VB6, a caixa de mensagem exibe o valor 5. Isto ocorre porque no VB6, quando você passa x para foo, você está enviando o endereço de memória da variável x. Assim, quando foo modifica y para 5, ela muda o valor no mesmo endereço de memória para o qual x aponta e isto faz com que o valor de x mude também. Se você tentasse digitar este exemplo no VB .NET, você veria algo acontecer. Primeiro, naturalmente, você teria que adicionar parênteses ao redor de x na sua chamada a foo. Contudo, quando você tentasse digitar a definição de foo, o VB .NET automaticamente adicionaria a palavra chave ByVal na sua definição, assim ela terminaria parecendo-se com isto: Sub foo(ByVal y As Integer) Se você quisesse passar por referência, você teria que adicionar a palavra chave ByRef por conta própria, ao invés de deixar o VB .NET usar o novo padrão de ByVal. Isto elimina muitos erros vistos por programadores novatos que não entenderam bem o conceito de passagem por referência em versões prévias do VB. Assim, no VB .NET, aceitando o padrão resulta em passagem de dados por valor. Se você rodar este código no VB .NET, você verá 3 na caixa de mensagem. Passagem de dados por valor é um benefício para aqueles que vão chamar rotinas cruzando fronteiras de processos, algo muito comum em aplicações distribuídas. A razão de ser um benefício depende de como você passa parâmetros entre componentes. Se você tem duas rotinas no mesmo processo - que é o que ocorre mesmo quando você usa dlls locais -, e uma rotina passa um parâmetro para a outra ByRef, ambas as rotinas podem acessar o mesmo endereço de memória. Contudo, se você agora move uma destas rotinas para um outro processo, elas não podem acessar o mesmo endereço de memória (ao menos não com facilidade). Assim, mesmo se você passar um parâmetro para uma rotina fora do processo usando ByRef, a rotina chamada receberá uma cópia dos dados e reservará memória para eles. E qualquer mudança feita nos dados pela rotina chamada tem que ser reconduzida para a rotina chamadora, o que requer uma lenta e custosa viagem dos dados entre fronteiras de processos. Passando parâmetros para rotinas fora do processo usando ByVal, você não tem que reconduzir as mudanças para a rotina chamadora. Muitos desenvolvedores criaram componentes para rodar em processos externos no VB (out-of-process), mas passando valores ByRef, não se apercebendo ser mais lento do que a passagem ByVal. O padrão do VB .NET de passagem de parâmetros ByVal deve evitar muitos destes enganos. Escopo a Nível de Bloco O VB .NET acrescenta a possibilidade de criar variáveis que são visíveis apenas dentro de um bloco. Um bloco é qualquer seção de código que termina com uma das palavras: End, Loop ou Next. Isto significa que blocos For...Next, If...End podem ter suas próprias variáveis. Veja o seguinte código: While y < 5 Dim z As Integer ... End While A variável z é visível apenas dentro do loop While. É importante perceber que, embora z seja visível apenas dentro do loop While, seu tempo de vida é o mesmo do procedimento. Isto significa que, se você reentrar no loop, z terá o mesmo valor de quando você saiu. Portanto, diz-se que o escopo de z é de nível de bloco, mas o seu tempo de vida é de nível de procedimento. `While...Wend` Torna-se `While...End While` O loop While ainda é suportado, mas o fechamento do loop agora é End While ao invés de Wend. Se você digitar Wend, o editor automaticamente muda para End While. Esta mudança finalmente modifica o loop While para finalmente se assemelhar a outras estruturas de bloco do VB, as quais sempre terminam com uma sintaxe de End <bloco>. Mudanças em Procedimentos O VB .NET tem mudanças que afetam como você define e trabalha com procedimentos. Algumas destas mudanças são mencionadas nesta seção. Argumentos Opcionais Requerem um Valor Padrão No VB6, você podia criar um argumento opcional ( ou vários argumentos opcionais ) quando definia um procedimento. Você poderia, opcionalmente, passar um valor por eles. Se você não atribuísse um valor padrão e o chamador não passasse um valor, a única forma de você checar isto era pela chamada a IsMissing. `IsMissing` não mais é suportado, porque o VB .NET não deixará que você crie argumentos opcionais que não tenham um valor default. IsMissing não é mais necessário, porque um argumento opcional terá sempre um valor. Por exemplo, sua declaração poderia parecer-se com isto: Sub foo(Optional ByVal y As Integer = 1) Note que a palavra Opcional é exibida, do mesmo modo que no VB6. Isto significa que o parâmetro não precisa ser passado. Contudo, se ele não for passado, y recebe o valor default de 1. Se a rotina chamadora passar um valor, naturalmente, y terá o valor que for passado nele. `Static` Não Mais Suportada em Subs ou Functions No VB6, você por Static na declaração de um sub ou função. Assim fazendo, tornava estáticas todas as variáveis naquele sub ou função, o que significa que elas reteriam o valor entre chamadas à rotina. Por exemplo, isto era aceito no VB6: Static Sub foo() Dim x As Integer Dim y As Integer x = x + 1 y = y + 2 End Sub Neste exemplo, x reterá seu valor entre chamadas. Assim, a segunda vez que esta rotina for chamada, x já terá um valor de 1 e o valor de x será incrementado para 2. A variável y terá o valor de 2 e, portanto, na segunda vez será incrementada para 4 O VB .NET não suporta mais a palavra chave Static na frente de declarações de sub ou function como fazia o VB6. Se você quiser que todas as variáveis de um sub ou function sejam estáticas, você terá que colocar a palavra chave Static na frente da declaração de cada variável. No VB .NET, o sub equivalente ao do exemplo anterior seria: Sub foo() Static x As Integer Static y As Integer x = x + 1 y = y + 2 End Sub O Comando `Return` O comando Return pode ser usado para produzir um imediato retorno de uma rotina chamada e, opcionalmente retornar um valor. Por exemplo, veja neste bloco de código: Function foo() As Integer While True Dim x As Integer x += 1 If x >= 5 Then Return x End If End While End Function Neste código, você entra no que se costuma chamar de loop infinito pela instrução While True. Dentro do loop, você declara um integer chamado x e começa a incrementá-lo. Você testa o valor de x e quando ele se torna maior que 5, você chama Return e retorna o valor de x. Isto causa um retorno imediato, o que significa que o programa não espera chegar no End Function para retornar. A velha forma de retorno ainda funciona. Por exemplo, para reescrever este procedimento usando o modo antigo de retornar um valor, você faria como no código a seguir. Você atribuiria o valor a ser retornado ao nome do procedimento foo. Então você sairia do loop e chegaria ao End Function. Você também poderia usar Exit Function ao invés de Exit Do. Function foo() As Integer Do While True Dim x As Integer x += 1 If x >= 5 Then foo = x Exit Do End If Loop End Function ParamArrays São Agora Passadas ByVal Uma matriz de parâmetros, ou ParamArray, é usada quando você não sabe quantos valores você irá passar para um procedimento. Uma ParamArray permite um número ilimitado de argumentos. Uma matriz de parâmetros automaticamente se redimensiona para conter o número de elementos que você está passando. Um só pode ter uma ParamArray e ela deve ser o último argumento na definição. A matriz deve ser de uma dimensão e todos os elementos devem ser do mesmo tipo de dados. Contudo, o tipo de dados padrão é Object, que é o que substitui o tipo de dados Variant no VB .NET. No VB6, todos os elementos de uma ParamArrary eram passados ByRef. Isto não podia ser modificado. No VB .NET, todos os elementos são passados ByVal. Isto também não pode ser mudado. Novamente, ByRef era usado em versões anteriores do VB por ser mais eficiente para passar parâmetros dentro de um programa em que tudo estivesse rodando no mesmo espaço de aplicação. Quando estiver trabalhando com componentes out-of-process, contudo, é mais eficiente passar parâmetros ByVal, como explicado anteriormente. Propriedade Podem Ser Modificadas Para ByRef No VB6, se uma classe tivesse uma propriedade , você poderia passar aquela propriedade para um procedimento como argumento. Se você passasse a propriedade ByVal, naturalmente, o valor seria apenas copiado. Se você a passasse ByRef, a rotina chamada poderia modificar o valor, mais o novo valor não seria refletido de volta na propriedade do objeto. No VB .NET, contudo, você pode passar uma propriedade para um procedimento ByRef e qualquer mudança feita no valor pela rotina chamada será refletida no valor da propriedade do objeto. Veja o seguinte exemplo em VB .NET. Não se incomode se a sintaxe da classe parecer estranha; apenas observe que você está criando uma classe chamada Test com a propriedade Name. Você instancia a classe no procedimento de evento Button1_Click e passa a propriedade Name por referência para foo. foo modifica a variável e termina. Você então usa uma caixa de mensagem para exibir a propriedade Name. Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim x As New Test() foo(x.Name) MsgBox(x.Name) End Sub Sub foo(ByRef firstName As String) firstName = "Torrey" End Sub Public Class Test Dim firstName As String Property Name() As String Get Name = firstName End Get Set(ByVal Value As String) firstName = Value End Set End Property End Class Quando você rodar este exemplo, a caixa de mensagem irá exibir "Torrey". Isto mostra que a propriedade do objeto está sendo passada por referência e que as mudanças na variável são refletidas de volta na propriedade do Objeto. Isto é novo no VB .NET. Mudanças em Matrizes As matrizes sofreram algumas mudanças também. As matrizes podiam ser algo confusas nas versões anteriores do VB. O VB .NET procura eliminar qualquer confusão pela simplificação das regras e removendo a capacidade de ter limite inferior diferente de zero. Limite Inferior É Sempre Zero O VB6 permitia que você tivesse um limite inferior em suas matrizes diferente de zero de duas maneiras. Primeiro,você poderia declarar uma matriz para um certo intervalo. Se você quisesse uma matriz iniciando de 1, você a declararia desta forma: Dim y(1 To 3) As Integer Isto iria criar uma matriz com três elementos indexados 1-3. Se você não quisesse este método, você poderia usar Option Base, que lhe permitiria atribuir o limite inferior padrão tanto para 0 (o padrão) ou 1. O VB .NET remove estas opções para você. Você não pode usar a sintaxe 1 to x e Option Base não mais é suportado. Na verdade, porque o limite inferior de matrizes é sempre 0, a função LBound não é mais suportada no VB .NET. Atribuição de Matrizes é ByRef ao Invés de ByVal No VB6, se você tivesse duas variáveis do tipo matriz e atribuísse uma à outra, você estaria criando uma cópia da matriz atribuída. Agora, no VB .NET, atribuindo uma matriz a outra, você está apenas atribuindo uma referência à matriz. Para copiar uma matriz, você usa o método Copy do objeto Array. `Option Strict` `Option Strict` é uma nova declaração que desabilita qualquer conversão automática de tipo que resultaria em perda de dados. Você passa a poder fazer apenas conversões de um tipo menor para um tipo com maior capacidade de armazenamento. Você não poderá converter, por exemplo, de um Long para um Integer, mas poderá converter de um Integer para um Long. Option Strict também impede a conversão entre números e strings. `Option Strict` é desligado por default. Para ligá-lo, vá ao topo da janela de código e digite Option Strict On. Se você for ao topo de um módulo e digitar Option Strict On, você poderá ver que o IDE reconhecerá potenciais problemas de conversão de tipos com as seguintes linhas de código: Dim longNumber As Long Dim integerNumber As Integer integerNumber = longNumber Com este código, o IDE irá sublinhar longNumber e uma tooltip irá dizer lhe o seguinte: `Option Strict disallows implicit conversions from Long to Integer`. Nota Eu recomendo fortemente que você use `Option Strict On` e `Option Explicit On`. Ligando os dois pode ajudar a reduzir erros que podem ocorrer apenas em tempo de execução. Mudanças em Tipos de Dados Há várias mudanças em tipos de dados que são importantes de apontar. Estas mudanças podem ter um impacto na performance e na utilização de recursos do seu código. Os tipos de dados no VB .NET correspondem aos tipos de dados no namespace System, que é importante para interoperabilidade entre linguagens. Todas as Variáveis São Objetos Tecnicamente, no VB .NET, todas as variáveis são derivadas da classe base Object. Isto significa que você pode tratar todas as variáveis como objetos. Por exemplo, para saber o comprimento de uma string, você pode usar o seguinte código: Dim x As String x = "Hello, World" MsgBox(x.Length) Isto significa que você pode tratar x como um objeto e examinar sua propriedade Length. Outras variáveis têm outras propriedades e métodos. Por exemplo, um Integer tem o método ToString que você pode usar e irá vê-lo num momento. `Short`, `Integer`, e `Long` No VB .NET, o tipo de dados Short é um inteiro de 16 bits, o Integer é um inteiro de 32 bits e o Long um inteiro de 64 bits. No código VB6, o Integer era um inteiro de 16 bits e um Long era um inteiro de 32 bits. Tornou-se comum para os programadores de VB usar o Long no lugar do Integer, porque o Long oferecia melhor performance em sistemas operacionais de 32 bits que o Integer. Contudo, em sistemas operacionais de 32 bits, inteiros de 32 bits são mais rápidos que inteiros de 64 bits. Portanto, no VB .NET, você preferirá voltar a usar o tipo de dados Integer ao invés do Long. Conversão Automática String/Número Não Suportada por `OptionStrict` No VB6, era fácil converter de números para strings e vice-versa. Por exemplo, examine este bloco de código: Dim x As Integer Dim y As String x = 5 y = x No VB6, não havia nada de errado com este código, O VB iria tomar o valor de 5 e automaticamente convertê-lo para uma string "5". O VB .NET, contudo, desabilita este tipo de conversão se Option Strict estiver em On. Ao contrário, você terá que usar CStr para converter um número para uma string, ou a função Val para converter uma string para um número. Você poderia reescrever o código anterior para o VB .NET da seguinte forma: Dim x As Integer Dim y As String x = 5 y = CStr(x) y = x.ToString ' Isto é equivalente à linha anterior Strings de Tamanho Fixo Não Mais Suportadas No VB6, você poderia declarar um string de comprimento fixo usando um declaração como esta: Dim y As String * 30 Isto declararia `y` para ser uma string de comprimento fixo que armazenaria 30 caracteres. Se você tentar usar este mesmo código no VB .NET, você produzirá um erro. Todas as strings no VB .NET são de tamanho variável. Todas as Strings São Unicode Se você se cansou de preocupar-se com a passagem de strings do VB para certas chamadas de API que aceitam tanto strings ANSI ou Unicode, você ficará feliz de ouvir que todas as strings no VB .NET são Unicode. O Valor de `True` Desde o Visual Basic 1.0, o valor de True no VB e no Access tem sido igual a -1. A .NET Framework declara que o valor de True é igual a 1. Isto parece ser um conflito entre o VB .NET e o .NET Framework subjacente. |Na verdade, 0 é False e qualquer valor é True no VB .NET.Contudo, se você perguntar pelo valor de True, você receberá -1 dentro do VB .NET. Você pode verificar que qualquer valor diferente de zero é True usando o seguinte código: Dim x As Integer x = 2 If CBool(x) = True Then MsgBox("Value is True") End If Se você atribuir a x qualquer valor positivo ou negativo, a função CBool irá retornar True. Se você atribuir 0 a x, CBool irá retornar False. Mesmo True sendo exibido como -1 no VB .NET, se você passá-lo para qualquer outra linguagem .NET, ela irá vê-lo como 1. Isto significa que todas as outras linguagens o vêem com o valor correto. Assim como aconteceu com o modo como as matrizes são dimensionadas, a Microsoft originalmente disse que o valor True, no VB .NET, iria mudar para 1. Contudo, muitos programadores em VB reclamaram e a Microsoft decidiu deixar True como -1. Isto é uma vergonha, mas não porque True deveria ser 1. É uma vergonha, porque significa que muitos programadores têm escrito código que usa o valor -1 ao invés de usar a constante True. É importante entender isto: não escreva código para o valor (1 ou -1). Apenas use a constante True e você irá evitar muitos problemas. O Tipo de Dados `Currency` Foi Trocado O tipo de dados `Currency` foi trocado pelo tipo de dados Decimal. O tipo de dados Decimal é um inteiro de 12 bytes sinalizado que pode ter até 28 dígitos à direita do ponto decimal. Ele suporta maior precisão do que o Currency e foi criado para aplicações que não podem tolerar erros de arredondamento. O tipo de dados Decimal tem um equivalente direto na .NET Framework, o que é importante para interoperabilidade de linguagens. O Tipo de Dados `Variant` Foi Trocado O tipo de dados `Variant` foi trocado. Antes que você comece a pensar que não há mais um tipo de dados para qualquer valor, entenda que o tipo de dados Variant foi trocado pelo tipo de dados Object. O tipo de dados Object ocupa apenas quatro bytes porque tudo que ele armazena é um endereço de memória. Portanto, mesmo se você atribuir um inteiro à variável, a variável Object ocupa quatro bytes que apontam para o endereço de memória usado para armazenar o inteiro. O inteiro é armazenado na memória. Ele irá ocupar oito bytes mais quatro bytes para o inteiro, consumindo assim doze bytes. Examine o código a seguir: Dim x x = 5 Neste caso, x é uma variável do tipo Object. Quando você atribui 5 a x, o VB .NET armazena o valor 5 em outra localização de memória e x armazena o endereço desta localização de memória. Quando você tenta acessar x, uma pesquisa é requerida para localizar o endereço de memória e recuperar o valor. Portanto, do mesmo modo que o Variant, o tipo de dados Object é mais lento que usar tipos de dados explicitos. Um tipo de dados Object pode ser `Nothing`. Em outras palavras, ele pode conter nada, o que significa dizer que ele pode não apontar para endereço algum de memória. Você pode testar por Nothing usando o operador de comparação (=), usando a função IsNothing ou perguntando se o objeto Is Nothing. Todas as três opções são mostradas no código a seguir: Dim x If x = Nothing Then MsgBox("Nothing") If IsNothing(x) Then MsgBox("Ainda nothing") If x Is Nothing Then MsgBox("E Ainda Nothing") Tratamento de Erros Estruturado O tratamento de erros mudou no VB .NET. Na verdade, a antiga sintaxe ainda funciona, mas há uma nova estrutura chamada `Try...Catch...Finally` que remove a necessidade de usar a antiga estrutura `On Error Goto.` A idéia geral da estrutura `Try...Catch...Finally` é por o código que pode causar o erro na porção Try e então pegar o erro. Dentro da porção Catch, você trata o erro. A porção Finally executa o código que vem após os comandos incluídos no Catch serem executados, independentemente de ter ou não havido um erro. Eis aqui um exemplo simples: Dim x, y As Integer ' Ambos serão Integers Try x \= y ' causa divisão por zero Catch ex As Exception msgbox(ex.Message) End Try Note A parte `ex As Exception` do comando `Catch` é opcional. Aqui você tem duas variáveis que são ambas Integers. Você tentou dividir x por y, mas como y ainda não tinha sido inicializada, ela continha o padrão 0. Esta divisão por zero gerou um erro e você o pegou na linha seguinte. A variável ex é do tipo Exception, que contém um erro que acabou de ocorrer. Assim, você simplesmente imprime a propriedade Message do mesmo modo como imprimia Err.Description no VB6. Na verdade, você ainda pode usar Err.Description e o objeto Err no geral. O objeto Err irá pegar quaisquer erros que sejam gerados. Por exemplo, assuma que sua lógica dita que um erro seja gerado se o saldo da conta de alguém se tornar muito baixo, e um outro erro é gerado se o saldo se torna negativo. Examine o código seguinte: Try If bal < 0 Then Throw New Exception("Saldo é negativo!") ElseIf bal > 0 And bal <= 10000 Then Throw New Exception("Saldo é negativo; cobrar juro") End If Catch MessageBox.Show("Error: " & Err.Description) End Try Neste caso, sua lógica de negócio diz que se o saldo cair abaixo de zero, você gera um erro informando o usuário que o saldo está abaixo de zero. Se o saldo cair abaixo de 10.000 mas permanece acima de zero, você notifica o usuário para iniciar a cobrança de juro. Neste caso, Err.Description pega a descrição que você atribuir na sua Exception. Você pode também ter múltiplos comandos Catch para pegar vários erros. Para ter um Catch para um erro em particular, você adiciona uma cláusula When ao Catch. Examine este código: Dim x As Integer Dim y As Integer Try x \= y ' causa divisão por zero Catch ex As Exception When Err.Number = 11 MsgBox("Você tentou dividir por zero") Catch ex As Exception MsgBox("Serve como um Catch para todos os demais erros") End Try Neste exemplo, você está testando por um Err.Number igual a 11, que é o erro da divisão por zero. Portanto, se você tiver uma divisão por zero, você exibe a mensagem que diz "Você tentou dividir por zero". Contudo, se um erro diferente acontecer, você irá cair no Catch sem a cláusula When. Na verdade, um Catch sem uma cláusula When age como uma seção "else" ou "otherwise" para tratar tudo que não foi tratado antes. Note que o código não passa por todos os Catches; ele para no primeiro que encontra. No exemplo anterior, você gerou um erro com o número 11. Você verá a mensagem "Você tentou dividir por zero", mas você irá pulas o Catch genérico. Se você quiser rodar algum código no final, independente de que erro tenha ocorrido (ou, até mesmo, se nenhum erro ocorreu, você pode adicionar um comando `Finally`. O código para isto é o seguinte: Dim x As Integer Dim y As Integer Try x \= y ' causa divisão por zero Catch ex As Exception When Err.Number = 11 MsgBox("Você tentou dividir por zero") Catch ex As Exception MsgBox("Serve como um Catch para todos os demais erros") Finally MsgBox("Rodando o código em Finally") End Try Neste código, se você teve ou não um erro, o código na seção Finally será executado. Saindo de um `Try...Catch...Finally` Mais Cedo Às vezes, você quer sair de um `Try...Catch...Finally` antes do final. Para sair antecipadamente, use o comando Exit Try. Exit Try pode ser posto em qualquer bloco Catch ou no bloco Try, mas não no bloco Finally. Após o Exit Try, o código cai no bloco Finally. A sintaxe se parece com isto: Dim x As Integer Dim y As Integer Try x \= y ' causa divisão por zero Catch ex As Exception When Err.Number = 11 msgbox("Você tentou dividir por zero") Exit Try Catch ex As Exception msgbox("Serve como um Catch para todos os demais erros") Finally msgbox("Rodando o código em Finally") End Try Estruturas Substituem Tipos Definidos Pelo Usuário (UDTs) Tipos definidos pelo usuário, ou UDTs, eram o modo de criar tipos de dados personalizados no VB6. Você podia criar um novo tipo de dados que continha outros elementos nele. Por exemplo, você podia criar um tipo de dados Cliente usando esta sintaxe: Private Type Cliente Nome As String Pagamento As Currency End Type Você pode então usar este UDT na sua aplicação com um código como este: Dim comprador As Cliente comprador.Nome= "Martha" comprador.Pagamento= 20000 MsgBox(comprador.Nome & " " & comprador.Pagamento) A declaração Type não mais é suportada no VB .NET. Ela foi substituída pela declaração Structure. A declaração Structure tem algumas grandes mudanças, mas para recriar o UDT mostrado acima, a sintaxe é esta: Structure Cliente Dim Nome As String Dim Pagamento As Decimal End Structure Note que a única diferença real até aqui é que você tem de usar Dim em cada variável dentro da estrutura, o que é algo que você não tinha de fazer em declarações Type, mesmo com Option Explicit ligado. Note também que o Dim é o mesmo que Public aqui, significando que as variáveis são visíveis para qualquer instância da estrutura Estruturas têm muitos outros recursos, contudo. Uma das maiores diferenças é que estruturas podem suportar métodos. Por exemplo, você poderia adicionar o método Remessa à estrutura Cliente para calcular os custos de envio baseados na zona de entrega. Este código adiciona uma propriedade ZonaDeEntrega e uma função CustoDeEnvio: Structure Cliente Dim Nome As String Dim Pagamento As Decimal Dim ZonaDeEntrega As Integer Function CustoDeEnvio() As Decimal If ZonaDeEntrega > 3 Then Return 25 Else Return CDec(12.5) End If End Function End Structure Aqui, você tem uma função embutida chamada CustoDeEnvio. Para usar esta estrutura, seu código seria algo assim: Dim comprador As Cliente comprador.Nome = "Martha" comprador.Pagamento = 20000 comprador.ZonaDeEntrega = 4 msgbox(comprador.CustoDeEnvio) Neste caso , a mensagem exibirá um valor de 25. Se você pensar que isto se parece com uma classe, certamente é similar. Na verdade, estruturas podem ter propriedades, métodos e eventos. Há certos inconvenientes, contudo. Algumas das limitações incluem as seguintes: Você não pode herdar uma estrutura. Você não pode inicializar os campos dentro de uma estrutura. Por exemplo, este código é ilegal: Structure Cliente Dim Nome As String = "Martha" End Structure Propriedades são públicas por padrão, ao invés de privadas como são nas classes. Structures são tipos de valor e não tipos de referência. Isto significa que se você atrubuir uma variável de estrutura a outra variável de estrutura, você obtém uma cópia da estrutura e não uma referência para a estrutura. O código seguinte mostra que está ocorrendo uma cópia porque uma atualização em vendedor não atualiza comprador: Dim comprador As Cliente Dim vendedor ' tipo de dados Object vendedor = comprador vendedor .Nome = "Linda" MsgBox(comprador.Nome) MsgBox(vendedor.Nome) Note que o código acima não irá funcionar se você tiver Option Strict ligado. Se você quiser testar este código, você terá que entrar com Option Strict Off. ### Novos Itens Em adição às mudanças ao núcleo da linguagem e em algumas ferramentas, há alguns recursos completamente novos no VB .NET. As principais novidades incluem recursos como construtores e destrutores, namespaces, herança, sobrecarga, multitarefa livre e garbage collection. Esta não é uma lista completa de forma alguma, mas vale a pena discutir estes seis recursos em algum grau. Um recurso que deve ser mencionado já que é algo que você verá em toda parte é a auto-indentação. Tão logo você crie um bloco, como um Sub ou If, o IDE indenta a próxima linha de código automaticamente. ### Construtores e Destrutores Construtores e destrutores são conceitos provavelmente novos para programadores em VB6. Eles são similares aos eventos Class_Initialize e Class_Terminate que você tem nas classes de VB6. No seu nível mais simples, construtores e destrutores são procedimentos que controlam a inicialização e destruição de objetos, respectivamente. Os procedimentos Sub New e Sub Finalize substituem os métodos Class_Initialize e Class_Terminate do VB6. Diferente de Class_Initialize, Sub New é executado só uma vez, quando o objeto é criado.Sub New não pode ser chamado explicitamente, exceto em raras circunstâncias. `Sub Finalize` é chamado pelo sistema quando o objeto recebe atribuição de Nothing ou quando todas as referências ao objeto são desfeitas. Contudo, você não pode saber exatamente quando o Sub Finalize realmente será chamado, em razão do modo como o VB .NET usa garbage collection, que será discutido nas próximas seções. Uma das razões para haver construtores é que você pode criar um objeto e passar alguns parâmetros para inicialização. Por exemplo, assuma que você quer criar uma classe lidando com um curso de treinamento e você quer inicializar a classe com um número de curso de tal forma que ela possa encontrar certas informações sobre o curso num banco de dados. Na sua classe, você cria um método Sub New que aceita um argumento para a identificação do curso. A primeira linha dentro do Sub New é uma chamada a outro construtor, geralmente o construtor da classe base da qual esta se deriva. Felizmente o VB .NET lhe oferece uma forma fácil de chamar o construtor da classe base da sua classe atual: MyBase.New Se você quer criar uma classe para um curso de treinamento e inicializá-la com um identificador de curso, sua classe se parecerá talvez com isto: Class Course Sub New(ByVal CourseID As Integer) MyBase.New() FindCourseInfo(CourseID) ' localiza informações sobre o curso ... End Sub End Class Para chamar esta classe do código cliente, sua chamada irá se parecer com algo assim: Dim TrainingCourse as Course = New Course(5491) Como o Sub Finalize trabalha será coberto na seção sobre garbage collection, onde você irá ver o Sub Dispose. ### Namespaces Talvez um dos aspectos mais confusos do VB .NET para programadores em VB é o conceito de namespace. Um namespace é um modo fácil de organizar objetos em um assembly. Quando você tem muitos objetos, tais como no System namespace fornecidos pelo runtime, um namespace pode simplificar o acesso porque ele é organizado em uma estrutura hierárquica e objetos relacionados aparecem agrupados sob o mesmo nó. Por exemplo, digamos que você quer criar objetos, propriedades e métodos para um loja de animais. Você poderia criar o namespace PetStore. Você poderia então criar alguns subnamespaces. Por exemplo, você poderia vender animais vivos e suprimentos como duas categorias principais de produtos. Dentro da categoria de animais vivos, você poderia vender cães, gatos e peixes. Se isto lhe parece um modelo de objetos, pode ser pensado de modo semelhante. Contudo, na verdade não se trata de objetos. Você pode ter um namespace chamado PetStore.Animais.Caes e neste namespace você teria as classes e métodos necessários para lidar com cães. Se você quisesse saber que tipo de comidas de cães você tem no estoque, você poderia procurar no PetStore.Suprimentos.Caes. Onde estas classes existem fisicamente depende de você; elas podem estar todas dentro de um grande assembly, mas logicamente separadas nestes vários namespaces. Se você quiser usar objetos em um assembly sem ter de qualificá-los completamente toda vez, use um comando Imports. Assim fazendo você usa os nomes dos objetos sem ter que qualificar suas hierarquias completas de namespace. Por exemplo, quando você criou seu primeiro projeto VB .NET no capítulo 2, "Sua Primeira Aplicação VB .NET", você viu alguns comandos Imports no topo do módulo de código do form. Um destes comandos era Imports System.WinForms. Este comando tornou todas as classes, interfaces, estruturas, delegates e enumerações disponíveis para você sem que você tivesse que qualificá-los completamente. Isto significa que o seguinte código é legal: Dim x as Button Sem o `Imports System.WinForms`, sua linha de código teria que ser assim: Dim x as System.WinForms.Button Na verdade, até a `MsgBox` que você conhece e ama teria de ser substituída pela classe System.WinForms.MessageBox. A função MsgBox ainda funciona por razões de compatibilidade, mas ela é parte do namespace Microsoft.VisualBasic.Interaction , que contém um número de métodos correspondentes a funções de versões prévias do VB. Se você está se perguntando do porquê de a Microsoft ter sacado estes elementos da linguagem e os ter tornado parte do runtime, a razão é óbvia: qualquer linguagem voltada para o runtime, em qualquer plataforma, tem acesso à funcionalidade comum. Isto significa que você pode entrar no C# e ter uma caixa de mensagem simplesmente importando o System.WinForms e usando a classe MessageBox. A MessageBox é bastante poderosa - o método Show tem doze variações disponíveis graças à sobrecarga. Você irá aprender o que é sobrecarga em um instante. ### Criando Seu Próprio Namespace Você é livre para criar seu próprio namespace dentro de seus assemblies. Você pode fazê-lo simplesmente inserindo seu bloco Namespace... End Namespace. Dentro do bloco namespace, você pode ter estruturas, classes, enums, interfaces e outros elementos. Quando você nomeia o namespace, ele se torna possível de ser importado. Seu código poderia ser algo assim:: Namespace VolantTraining Public Class Customer 'código aqui End Class Public Class Student 'código aqui End Class End Namespace Namespaces podem ser aninhados dentro de outros namespaces. Por exemplo, seu namespace poderia se parecer com algo assim: Namespace TreinamentoVolante Namespace Cliente Class Trainamento ... End Class Class Consultoria ... End Class End Namespace Namespace Estudante ... End Namespace End Namespace Aqui, um namespace, `TreinamentoVolante`, contém dois outros namespaces: `Cliente` e `Estudante`. O namespace `TreinamentoVolante.Cliente` contém as classes `Trainamento` e `Consultoria`, e você pode ter clientes que usam seus serviços de treinamento e clientes que usam seus serviços de consultoria Se você escolher não criar explicitamente namespaces, todas as suas classes e módulos ainda pertencem a um namespace. Este namespace é o namespace padrão e é o nome do seu projeto.Você pode ver este namespace e mudá-lo se quiser, basta ir à caixa de diálogo Project Properties para o seu projeto. Se você declara um namespace dentro do seu projeto, ele é subordinado a este namespace raiz. Portanto, se um namespace raiz da sua aplicação for Project1, o namespace completo para TreinamentoVolante é Project1.TreinamentoVolante. ### Herança O recurso mais requisitado para ser adicionado ao VB por anos tem sido a herança. A Microsoft frequentemente afirmou que o VB já possuia herança; o VB suportava herança de interface, o que significa que você podia herdar uma interface usando o comando Implements. Contudo, as interfaces herdáveis no VB não podiam ter qualquer código nelas, ou se tivesse, o código era ignorado na herança. Portanto, a classe que estivesse herdando a interface tinha que fornecer métodos para todos os métodos na interface. Se você tivesse mais de uma classe que herdasse a interface, você teria que reescrever o código em cada uma delas. Os programadores de VB queriam poder escrever o código de implementação da interface uma única vez em uma classe base e então herdá-lo em outras classes, que seriam chamadas de classes derivadas. As classes derivadas poderiam usar o código da classe base. Com o VB .NET, os programadores têem o queriam. Não apenas suas classes derivadas herdam as propriedades e métodos da classe base, elas podem estender os métodos e, naturalmente, criar novos métodos (apenas na classe derivada). As classes derivadas podem também substituir qualquer método na classe base com um novo método de mesmo nome, num processo chamado overriding. Formulários, que na verdade são apenas classes, podem ser herdados para criar novos formulários. Há muitos conceitos relacionados a herança e vê-la na prática é importante o bastante para justificar um capítulo para isto. O capítulo 5, "Herança com VB .NET", é todo sobre a herança e como usá-la no VB .NET. ### Sobre-Carregamento (Overloading) Sobre-carregamento é um outro recurso que alguns desenvolvedores VB têm requisitado por um longo tempo. Resumidamente, sobre-carregamento permite que você defina um mesmo procedimento múltiplas vezes. O procedimento tem o mesmo nome, mas uma diferente lista de argumentos de cada vez. Você poderia simular isto de uma forma deselegante no VB6. Você poderia passar um argumento como um Variant, e então usar o comando VarType para checar o tipo de variável que estava sendo passada. Isto era desajeitado e o código poderia se tornar inútil se você tivesse que aceitar uma matriz ou uma coleção. O VB .NET lhe dá um modo ótimo para lidar com isto. Imagine que você tem um procedimento capaz de aceitar strings ou inteiros. A funcionalidade dentro do procedimento será totalmente diferente dependendo se o que é passado for uma string ou um inteiro. O seu código se parecerá com isto: Overloads Function FindCust(ByVal psName As String) As String ' pesquisa pelo valor de psName End Function Overloads Function FindCust(ByVal piCustNo As Integer) As String ' pesquisa por piCustNo End Function Você tem agora uma função chamada `FindCust` que pode receber tanto um inteiro como uma string. Suas chamadas a ela podem ser feitas assim: Dim x As String x = FindCust("Smith") x = FindCust(1) Se `Option Strict` estiver em On, você não consegue compilar uma chamada inválida. Por exemplo, não há nenhuma versão sobre-carregada de FindCust que aceita um valor de ponto flutuante de qualquer tipo. O VB .NET não permitirá que o código seguinte compile: x = FindCust(12.5) ### Multithreading Pela primeira vez, o VB NET deu aos desenvolvedores a possibilidade de escrever aplicações verdadeiramente multi-encadeadas (com multithreading). Se suas aplicações devem realizar uma tarefa que irá demorar um longo período, tal como percorrer um longo recordset ou realizar uma série de complexos cálculos matemáticos, você pode confinar aquele processo à sua própria thread (encadeamento) e ainda deixar que a sua aplicação esteja acessível para o usuário realizar outras tarefas. No VB6, o melhor que você poderia fazer para impedir que o resto de sua aplicação ficasse travado era usar o método DoEvents. Examine este código que foi escrito para VB .NET. Aqui você tem algum código para o Button4. Este código chama a rotina BeBusy (esteja ocupado), que tem um loop apenas para tomar tempo. Contudo, enquanto estiver neste loop você estará consumindo a thread para a aplicação e a interface com o usuário não responderá enquanto o loop estiver rodando. CUIDADO Isto levou por volta de oito segundos para rodar na minha máquina. Pode ser muito mais lento ou mais rápido na sua máquina. A boa nova é que o VB .NET IDE roda numa thread separada. Assim, se você se achar esperando para sempre, apenas clique no IDE e escolha Stop Debugging no menu Debug. Private Sub Button4_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button4.Click BeBusy() End Sub Sub BeBusy() Dim i As Decimal For i = 1 To 10000000 'nada faz, mas segura a aplicação Next Beep() End Sub Se você rodar este código e tentar clicar no formulário antes que BeBusy termine de rodar, você verá que o formulário não responde a nenhum evento. Por exemplo, outros botões não podem ser clicados enquanto BeBusy estiver rodando. Para que o formulário continue a responder enquanto BeBusy estiver rodando, você pode rodar BeBusy em uma thread separada. Para criar uma nova thread, você precisa usar a classe System.Threading.Thread. Na criação da classe, você passa o nome do procedimento ou método que você quer rodar naquela thread. Você prefixa o nome do procedimento ou método com o operador AddressOf. Seu código iria parecer com isto: Dim busyThread As New System.Threading.Thread(AddressOf BeBusy) Para corrigir o código e evitar que BeBusy continue consuminto a thread do programa principal, você criou agora uma nova thread e irá rodar BeBusy nesta thread. Contudo, aquela linha de código não é o suficiente. Em seguida, você deve chamar o método Start na nova thread. Com o VB .NET, para chamar BeBusy na sua própria thread ficaria assim: Private Sub Button4_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button4.Click Dim busyThread As New System.Threading.Thread(AddressOf BeBusy) busyThread.Start() End Sub Nenhuma mudança é necessária na rotina BeBusy. Se você rodar este código agora, a interface continuará ativa enquanto BeBusy estiver rodando. O Beep em BeBusy irá fazê-lo saber quando o procedimento chegar ao final. Há algumas limitações para usar mútli-encadeamento livre. Elas podem ser significativas, mas para cada uma delas há uma forma de contorno. Algumas destas limitações são: O procedimento ou método que você estiver rodando em uma nova thread não pode aceitar argumentos. Para resolver este problema, você tem duas escolhas. Você pode usar variáveis globais, mas esta não é uma solução elegante. Em vez disto, crie propriedades ou campos na classe cujo método você estiver chamando e atribua valores a estas propriedades após criar o objeto. Obviamente isto não funcionaria no exemplo anterior, porque a chamada era simplesmente um sub no mesmo programa. O procedimento ou método que você roda em uma nova thread não pode retornar um valor. Para solucionar este problema, você pode usar variáveis globais, mas novamente esta não é uma solução elegante. O principal problema é que sua aplicação terá que ficar checando a thread para saber quando ela terminou antes de poder usar com segurança a variável global. Ao invés disto, você deveria considerar a geração de um evento com o valor de retorno como um parâmetro do evento. A sincronização também é um problema com aplicações multi-encadeadas. Se você estiver realizando uma série de cálculos complexos em uma thread, outras partes da sua aplicação precisam esperar até que a thread termine antes que possam usar seus resultados. Você pode monitorar threads para ver quando elas terminam ou você pode ter métodos nas threads que disparam eventos para notificá-lo quando as threads terminam. VB .NET fornece a propriedade IsAlive para cada thread, desta forma você pode checar e ver quando a thread está rodando. Há muito mais sobre multithreading, e será coberto em detalhe no capítulo 11, "Multithreading no VB .NET." ### Garbage Collector (Coletor de Lixo) No VB6, se você atribuísse Nothing a um objeto, ele era destruído imediatamente. Isto não mais é verdade no VB .NET. Ao invés disto, quando você atribui Nothing a um objeto ou todas as referências a ele terminam, ele é marcado para a garbage collection. Ele ainda está na memória, consumindo recursos. O garbage collector roda em uma thread separada e procura de tempos em tempos por objetos para serem destruídos. O garbage collector trabalha apenas quando você começa a ficar sem recursos e se faz necessário liberar recursos destruindo objetos. No entanto, se o sistema estiver muito carregado, o tempo de demora para que o garbage collector entre em ação e destrua os objetos pode levar muitos segundos - uma eternidade em termos de CPU. Pelo fato de os objetos não serem destruídos quando você lhes atribui Nothing, a Microsoft chama isto de "finalização não determinística", porque o desenvolvedor não mais está no verdadeiro controle de quando o objeto será destruído. O método Sub Finalize é chamado quando o objeto é destruído pelo garbage collector. É importante entender que mesmo que um objeto tenha sido marcado pelo garbage collector os recursos que o objeto tenha aberto permanecem abertos, incluindo quaisquer dados ou arquivos que ele possa ter obtido. Por que o objeto ainda está na memória, mantendo referências abertas, cursos abertos, você poderia querer chamar explicitamente o garbage collector chamando o método Collect da classe GC no namespace System. Uma simples chamada GC.Collect força o garbage collector a entrar em ação e limpar quaisquer objetos que tenham sido marcados para coleta. Quando um objeto é destruído, Sub Finalize é chamado automaticamente. Contudo, uma convenção usada pelos desenvolvedores .NET é ter um Sub Dispose nas classes. O Sub Dispose é chamado especificamente, e libera todos os recursos. Uma vez que o código no Sub Dispose fecha todos os recursos abertos, ele age de modo parecido com a finalização determinística do VB6. Seguindo esta convenção, o seu Sub Dispose irá fechar todos os recursos abertos, tais como arquivos, conexões a bancos de dados, e outros objetos. Portanto, você não tem que chamar diretamente o garbage collector, pois todos os recursos do objeto já foram liberados. Para ver a coleta de lixo em ação, crie uma nova aplicação Windows e nomeie-a GCTest. No formulário, adicione dois botões. Adicione um componente ao projeto e nomeie-o Garbage. Dentro do componente Garbage, adicione o código seguinte: Protected Overrides Sub Finalize() Beep() End Sub Isto apenas adiciona um som de bip quando o objeto é removido da memória pelo garbage collector. De volta ao formulário, adicione o seguinte código: Dim oGarbage As New Garbage() Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click oGarbage = Nothing End Sub Private Sub Button2_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button2.Click GC.Collect() End Sub Agora, rode o projeto. Quando você clica no `Button1`, você atribui Nothing ao objeto. Contudo, você não ouve o bip, porque o objeto não foi realmente removido da memória. Agora, clique no Button2, e você ouvirá um bip. Você forçou o garbage collector a entrar em ação e remover o objeto, o que faz com que o código em Sub Finalize seja executado. ### Mudanças no IDE Há inúmeras mudanças no IDE. Muitas das quais foram mostradas no capítulo 2: a falta dos controles Line e Shape, o novo criador de menus, a nova forma de atribuir a ordem de tabulação e a janela de help dinâmico. Uma área interessante são as mudanças disponíveis graças à biblioteca GDI+. Como mencionado anteriormente, os controles Line e Shape se foram. Você pode substituí-los com labels de altura 1 e bordas visíveis. Ou, você pode usar o namespace System.Drawing. Por exemplo, o System .Drawing tem uma classe Graphics que contém métodos como DrawCurve, DrawEllipse, DrawLine e DrawRectangle. Você realmente precisa de algum código para desenhar uma linha. Você precisa colocar o código no sub OnPaint para ter o código chamado automaticamente. Apenas crie um novo formulário e adicione o código seguinte, mas não esqueça de adicionar Imports System.Drawing no topo do formulário. Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) Dim LineMaker As Graphics LineMaker = e.Graphics Dim RedPen As New Pen(Color.Red, 3) Dim Point1 As New Point(30, 30) Dim Point2 As New Point(Me.Size.Width - 30, 30) LineMaker.DrawLine(RedPen, Point1, Point2) End Sub Você pode ver o resultado deste código na Figura 3.1. Figura 3.1. Um formulário com uma linha desenhada graças à GDI+. Apenas desenhar linhas com a GDI+ não é tão excitante. Com o VB .NET, você pode realmente criar formulários que não são retangulares. Para criar um formulário circular, crie um formulário e adicione um botão a ele. Digite Imports System.Drawing.Drawing2D no topo e adicione o seguinte código ao formulário: Private Sub Button1_Click_1(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim graPath As GraphicsPath = New GraphicsPath() graPath.AddEllipse(New Rectangle(0, 0, 200, 200)) Me.Region = New [Region](graPath) End Sub Rode o projeto e clique no botão. O form se torna um círculo, conforme mostrado na Figura 3.2. Figura 3.2. Um formulário circular graças à GDI+. ### Sumário Há várias mudanças na linguagem VB na transição para o VB .NET. É importante entendê-las à medida que você avança. Algumas mudanças são de menor importância, tais como a exigência de usar parênteses e a ausência de propriedades default. Mudanças mais significativas na lista são recursos como o tratamento de erros Try ... Catch ... Finally. Finalmente, há algumas mudanças críticas. Namespaces são as mudanças mais dramáticas que irão afetar o desenvolvedor VB .NET. Herança e threading livre também irão ser de grande importância no modo como as aplicações vão ser escritas. Agora que você viu muistas das mudanças significativas na própria linguagem, cada novo capítulo cobrirá um tópico em particular, mostrando tanto novas funcionalidades (como Windows Services e Web Services ) ou mudanças no modo como as coisas são feitas (tais como criação de classes e assemblies).