Expandir
Descobrindo os aperfeiçoamentos de linguagem do novo Visual Basic 2005

Matthew MacDonald

Os desenvolvedores da Microsoft estão trabalhando duro na nova versão do .NET e, afinal, o que seria uma nova plataforma de programação sem alguns ajustes de linguagem? Neste artigo, Matthew MacDonald descreve as mudanças preparadas para a mais nova versão do Visual Basic, atualmente chamada de Visual Basic 2005.

(Este artigo é baseado no mais recente livro de Matthew MacDonald, VB 2005 Developer's Notebook, da Editora O'Reilly.)

Quando o .NET 1.0 chegou ao mercado, ele revolucionou o modo como os desenvolvedores criavam os aplicativos modernos e introduziu mudanças drásticas na linguagem Visual Basic pela primeira vez em quase uma década. Por exemplo, tarefas comuns, como criar objetos, declarar estruturas, mostrar formulários e usar matrizes, foram alteradas consideravelmente. (Para consultar a pesquisa das mudanças do Visual Basic 6.0 para o Visual Basic .NET, a partir de uma perspectiva um tanto pessimista, vá para www.mvps.org/vb/index2.html?rants/vfred.htm – em inglês.)

Felizmente, o .NET 2.0 não pretende nenhuma reação semelhante. Ao contrário, ele aprimora a linguagem sem comprometer nenhum código existente. Muitas mudanças parecerão uma repetição dos recursos de C#, embora haja alguns poucos recursos completamente novos nas duas linguagens. Neste artigo, você conhecerá as alterações mais úteis, incluindo:

Nesta página

Sobrecarga de operador
Classes parciais
A instrução Continue
A instrução Using
Acessibilidade de propriedade dividida
Coleções genéricas
Conclusão

Um recurso que não será discutido neste artigo é o novo My object, uma espécie de repositório central para funcionalidade útil. O My object não é exatamente uma mudança na linguagem Visual Basic. É mais um conjunto de atalhos agrupados e protegidos no VB. Fique ligado nos próximos artigos que falarão um pouco mais sobre o My object.

Sobrecarga de operador

Você conhece os operadores aritméticos, como os usados para adição (+), subtração (-), divisão (/) e multiplicação (*). No VB.NET 1.0, esses operadores são aplicados a tipos numéricos e geralmente não têm nenhum significado quando são usados com outros objetos. No entanto, esse não é o caso no C#. Ali, os desenvolvedores podem criar classes personalizadas que dão suporte a todos esses operadores (e alguns outros) para seu próprio uso.

Um dos desenvolvedores do compilador do VB.NET resumiu uma vez que "a sobrecarga de operador não foi incluída no VB.NET 1.0, porque, infelizmente, o tempo não foi suficiente". O VB 2005 preenche essa lacuna.

Para compreender como a sobrecarga de operador funciona, seria bom olhar um exemplo simples. Considere o seguinte código, que não seria válido no VB.NET 1.0.

Dim ShortSpan, LongSpan, TotalSpan As TimeSpan
ShortSpan = TimeSpan.FromMinutes(100)
LongSpan = TimeSpan.FromDays(14)
TotalSpan = ShortSpan + LongSpan
Console.WriteLine(TotalSpan.TotalMinutes)

A linha importante é a penúltima, que usa o operador + para adicionar dois objetos TimeSpan. A razão pela qual isso funciona é porque, quando analisa a expressão "ShortSpan + LongSpan", o compilador descobre que o tipo de ShortSpan é um TimeSpan, e que TimeSpan define o operador + (que é justamente uma função especial). Com esse conhecimento, ele converte esse código em algo semelhante ao seguinte (que deve ser uma sintaxe válida no VB.NET 1.0):

TotalSpan = ShortSpan.Add(LongSpan)

Para sobrecarregar um operador no Visual Basic 2005, você precisa criar um método especial de operador na sua classe (ou estrutura). Esse método deve ser declarado com as palavras-chave Public Shared Operator, seguidas do símbolo para o operador (+). Por exemplo, este é um método de operador que adiciona suporte para o operador de adição (+):

Public Shared Operator+(objA As MyClass, 
  objB as MyClass) As MyClass
    ' (Code goes here.)
End Operator

Todo método de operador aceita dois parâmetros, que representam os valores nos dois lados do operador. Dependendo da classe e do operador, a ordem pode ser importante (assim como o é para a divisão).

A sobrecarga de operador pode não fazer sentido para muitos objetos comerciais, mas é extremamente prática no caso de você precisar modelar estruturas matemáticas, como vetores, matrizes, números complexos ou frações. A Listagem 1 mostra como sobrecarregar os operadores aritméticos do Visual Basic, para que você possa adicionar e subtrair um objeto Point básico (como o definido nos espaços para nome do GDI+ do .NET Framework).

Listagem 1. Uma classe Point com sobrecarga de operador.

Public Class Point

    Public X As Integer
    Public Y As Integer

    Public Sub New(ByVal x As Integer, _
      ByVal y As Integer)
        Me.X = x
        Me.Y = y
    End Sub

    Public Shared Operator +(ByVal x As Point, _
      ByVal y As Point) As Point
        Return New Point(x.X + y.X, x.Y + y.Y)
    End Operator

    Public Shared Operator -(ByVal x As Fraction, _
      ByVal y As Fraction) As Point
        Return New Point(x.X - y.X, x.Y - y.Y)
    End Operator

    ' Get a string representation of the Point.
    Public Overrides Function ToString() As String
        Return "(" & Me.X.ToString & ", " & _
          Me.Y.ToString & ")"
    End Function

End Class

Geralmente, os parâmetros e o valor de retorno de um método de operador usam o mesmo tipo. No entanto, é possível criar mais de uma versão de um método de operador (sobrecarga), de modo que o seu objeto possa ser usado em expressões com tipos diferentes.

Classes parciais

Classes parciais não são tão elaboradas quanto a sobrecarga de operador, mas são igualmente úteis. Com classes parciais, você pode dividir uma classe em arquivos separados. Você só precisa definir a mesma classe em mais de um local, e incluir a palavra-chave Partial. Segue um exemplo que define uma classe chamada TestClass em duas partes:

Partial Public Class TestClass
    Public Sub TestMethodA()
        Console.WriteLine("Method A called.")
    End Sub
End Class

Partial Public Class TestClass
    Public Sub TestMethodB()
        Console.WriteLine("Method B called.")
    End Sub
End Class

Nesse exemplo, as duas declarações estão no mesmo arquivo, uma após a outra. No entanto, você pode colocar as duas partes de TestClass em arquivos de código-fonte diferentes no mesmo projeto. (A única restrição é que não se pode definir as duas partes em conjuntos de módulos (assemblies) separados nem em espaços para nome separados.) Também não há razão que impeça a criação de mais partes TestClass: na essência, não há limites.

Ao criar o aplicativo, o Visual Studio .NET acompanha cada parte de TestClass e monta as partes em uma classe completa e compilada com dois métodos, TestMethodA() e TestMethodB(). Você pode usar os dois métodos da mesma maneira:

Dim Obj As New TestClass()
Obj.TestMethodA()
Obj.TestMethodB()

De uma maneira geral, as classes parciais têm dois propósitos:

  • Elas podem ajudar a dividir classes extremamente grandes em partes menores mais gerenciáveis.

  • Elas podem ocultar códigos menos importantes, como o código do designer gerado automaticamente.

O segundo ponto é realmente o mais importante, e é a razão pela qual as classes parciais foram introduzidas nas linguagens .NET, como o VB 2005 e o C#. Por exemplo, quando você cria um formulário .NET no Visual Studio 2005, o seu código de manipulação de eventos é colocado no arquivo de código-fonte para o formulário, mas o código do designer que cria e configura cada controle e conecta os manipuladores de eventos não fica mais visível. Esse código é colocado em um arquivo separado e tirado do campo de visão para que você não o viole inadvertidamente. (Obviamente, você ainda poderá ver esse código se desejar confirmar que ele está realmente ali. Selecione Project (Projeto) | Show All Files (Mostrar Todos os Arquivos) no menu do Visual Studio. Ao selecionar, um novo arquivo é exibido com a outra metade da classe. Por exemplo, se você criar um novo formulário chamado Form1, terá na verdade um arquivo Form1.vb para o código e um arquivo Form1.Designer.vb que contém a parte gerada automaticamente. Um último aviso: isso ainda não está implementado em todas as versões alfa do Visual Studio 2005!)

A instrução Continue

A linguagem Visual Basic possui várias instruções de controle de fluxo que permitem direcionar a execução. Por exemplo, você pode usar Return para sair de uma função ou Exit para sair de um loop. No entanto, até o VB 2005, não havia nenhuma maneira de ir para a próxima iteração de um loop. O VB 2005 adiciona esse ingrediente com a nova instrução Continue.

Continue é um daqueles detalhes da linguagem que parecem sem importância à primeira vista, mas rapidamente provam ser de grande utilidade. A instrução Continue existe nas três versões, Continue For, Continue Do e Continue While, cada uma usada com um tipo diferente de loop (For ... Next, Do ... Loop ou While ... End While).

Para ver o funcionamento da instrução Continue, considere o seguinte código:

For i = 1 to 1000
    If i Mod 5 = 0 Then
        ' (Task A code.)
        Continue For
    End If
    ' (Task B code.)
Next

Esse código executa um loop 1.000 vezes, incrementando um contador i. Sempre que i for divisível por 5, Task A code será executado. Em seguida, a instrução Continue For será executada, o contador será incrementado e a execução continuará no início do loop, ignorando o código em Task B.

Nesse exemplo, a instrução Continue não é realmente necessária, porque você poderia reescrever o código usando uma lógica condicional mais sofisticada. No entanto, se você tiver uma seção complicada do código em que seja necessário executar uma série de testes diferentes, usar a instrução Continue para sair rapidamente por alguns instantes poderá ser muito útil.

Existe uma limitação da instrução Continue: ela pode não funcionar corretamente em um loop aninhado. Se você aninhar um loop dentro de outro loop do mesmo tipo, não existe uma maneira inequívoca de se referir ao loop externo e, portanto, a instrução Continue sempre se referirá ao loop interno.

No entanto, se você tiver dois tipos diferentes de loop, não haverá problema. Por exemplo, se você aninhar um loop For dentro de um loop Do, poderá usar Continue For para pular para a próxima iteração do loop interno, ou Continue Do para pular para a próxima iteração do loop externo, conforme mostrado aqui:

Do
    For i = 1 to 1000
        If i Mod 5 = 0 Then
            ' (Task A code.)

            ' The next line skips Task B and Task C.
            Continue Do
        End If
        ' (Task B code.)
    Next
    ' (Task C code.)
Loop Until StopLoop

Essa técnica também funciona de maneira invertida (com um loop Do dentro de um loop For).

A instrução Using

No .NET, é fundamental garantir que os objetos que usam recursos não gerenciados (como identificadores de arquivo, conexões de banco de dados, contextos de gráficos etc.) liberem esses recursos o mais rápido possível. Com essa finalidade, esses objetos sempre implementam a interface IDisposable e fornecem um método Dispose() que você pode chamar para liberar seus recursos imediatamente.

O único problema é que você precisa lembrar de chamar o método Dispose() (ou outro método que chame Dispose(), por exemplo, o método Close()). O VB 2005 fornece uma nova proteção que você pode usar para garantir que Dispose() sempre será chamado: a instrução Using.

A instrução Using é usada em uma estrutura de bloco. Na primeira linha, ao declarar o bloco Using, você especifica o objeto descartável que está sendo usado. Normalmente, o objeto também é criado ao mesmo tempo usando a palavra-chave New. Em seguida, você escreve o código que usa o objeto descartável dentro do bloco Using. Segue um exemplo com um trecho de código que cria um novo arquivo e grava alguns dados no arquivo:

Using NewFile As New _
  System.IO.StreamWriter("c:\MyFile.txt")
    NewFile.WriteLine("This is line 1")
    NewFile.WriteLine("This is line 2")
End Using

' The file is closed automatically. 
' The NewFile object is no longer available here.

Nesse exemplo, assim que a execução sai do bloco Using, o método Dispose() é chamado no objeto NewFile, liberando o identificador de arquivo. Felizmente, o .NET garante que o recurso seja liberado, independentemente de como você saia do bloco Using e mesmo que ocorra uma exceção não tratada.

Acessibilidade de propriedade dividida

O VB reconhece três níveis de acessibilidade. São eles (do mais ao menos permissivo):

  • Public (disponível a todas as classes em todos os conjuntos de módulos (assemblies))

  • Friend (disponível a todos os códigos em todas as classes no conjunto de módulos (assembly) atual)

  • Private (somente disponível ao código na mesma classe)

Imagine que você esteja criando um componente de DLL que será usado por outro aplicativo. Você deve criar uma propriedade chamada Status necessária para que o aplicativo cliente possa ler e, em seguida, você deve tornar a propriedade Public:

Public Property Status() As Integer
    Get
        Return _Status
    End Get
    Set(ByVal value As Integer)
        _Status = value
    End Set
End Property

Nesse exemplo, os procedimentos das propriedades set e get têm a mesma acessibilidade, que é um requisito no VB.NET 1.0. O problema aqui é que o cliente pode alterar a propriedade Status, o que não faz sentido. Você pode criar uma propriedade somente leitura (em outras palavras, omitir completamente o procedimento da propriedade set), mas isso não permitiria a alteração de Status de outras classes no conjunto de módulos (assembly) do seu componente.

A solução do VB 2005 é dar ao procedimento da propriedade set o nível de acessibilidade Friend. O código deve ficar assim:

Public Property Status() As Integer
    Get
        Return _Status
    End Get
    Friend Set(ByVal value As Integer)
        _Status = value
    End Set
End Property

Coleções genéricas

Um dos princípios mais importantes de programação orientada a objetos é fazer suas soluções o mais abstratas e genéricas possível, de modo que elas possam ser reutilizadas em cenários diferentes. Infelizmente, isso não é sempre fácil. Um dos desafios que os programadores encontram é criar classes genéricas o suficiente para serem reutilizadas, mas específicas, para que possam reconhecer erros e garantir bom desempenho.

Por exemplo, pense em coleções. Um bom programador de orientação a objeto preferiria compilar uma classe genérica Collection que possa ser usada com qualquer tipo de objeto, em vez de uma coleção separada para cada tipo específico de item (como OrderItemCollection). A classe Collection genérica é mais simples, mais elegante e muito menos trabalhosa do que um conjunto de classes personalizadas como OrderItemCollection, por exemplo. Por outro lado, algumas considerações sobre desempenho e segurança de tipo tornam a solução genérica menos desejável. Em outras palavras, se você usar uma classe genérica Collection para armazenar objetos OrderItem, como você pode ter certeza de que ninguém inserirá outro tipo de objeto, causando um problema pernicioso no futuro?

O .NET 2.0 apresenta uma solução chamada coleções genéricas. As coleções genéricas são classes parametrizadas por tipo. Em outras palavras, você cria um modelo de classe que dá suporte a qualquer tipo. Ao instanciar essa classe, você especifica o tipo que deseja usar e, a partir desse momento, aquele objeto é "bloqueado" para o tipo escolhido. As coleções genéricas são internas do Common Language Runtime, portanto, só estão disponíveis para as linguagens que dão suporte a elas, incluindo VB e C#. (Para obter mais informações sobre a implementação de C# das coleções genéricas, consulte o artigo de Joe Mayo sobre o assunto nesta edição.)

Outro exemplo de onde as coleções genéricas fazem muito sentido é a classe System.Collections.ArrayList. ArrayList é uma coleção de propósito geral autodimensionável dinamicamente. Ela pode guardar objetos .NET comuns ou os seus próprios objetos personalizados. Para permitir isso, o ArrayList trata tudo como o tipo base Object. O problema é que não existe maneira de impor restrições ao funcionamento do ArrayList. Por exemplo, se você deseja usar o ArrayList para armazenar uma coleção de objetos Customer, não existe maneira de garantir que um código com defeito não insira seqüências, inteiros ou qualquer outro tipo de objeto acidentalmente, causando problemas futuros. Por essa razão, os desenvolvedores geralmente criam suas próprias classes de coleção de tipo seguro; na verdade, a biblioteca de classes do .NET possui dúzias deles.

As coleções genéricas podem resolver esse problema. Por exemplo, com as coleções genéricas, você pode declarar uma classe que funciona com qualquer tipo que use a palavra-chave Of:

Public Class GenericList(Of ItemType)
    ' (Code goes here)
End Class

Nesse caso, você está criando uma nova classe chamada GenericList que pode funcionar com qualquer tipo de objeto. No entanto, o cliente precisa especificar qual tipo deve ser usado. No seu código de classe, você se refere a esse tipo como ItemType. É claro que ItemType não é exatamente um tipo: é apenas um espaço reservado para o tipo que você escolherá quando criar um objeto GenericList. A Listagem 2 mostra o código completo para um ArrayList de tipo seguro.

Listagem 2. Uma coleção de tipo seguro usando as coleções genéricas.

Public Class GenericList(Of ItemType)
    Inherits CollectionBase

    Public Function Add(ByVal value As ItemType) _
      As Integer
        Return List.Add(value)
    End Function

    Public Sub Remove(ByVal value As ItemType)
        List.Remove(value)
    End Sub

    Public ReadOnly Property Item( _
      ByVal index As Integer) As ItemType
        Get
            ' The appropriate item is retrieved from 
            ' the List object and explicitly cast to 
            ' the appropriate type, and then returned.
            Return CType(List.Item(index), ItemType)
        End Get
    End Property
End Class

A classe GenericList envolve um ArrayList comum. No entanto, ela fornece métodos Add() e Remove() de tipo seguro, que usa o espaço reservado ItemType.

Segue um exemplo de como se pode usar a classe GenericList para criar uma coleção ArrayList que somente dá suporte a seqüências:

' Create the GenericList instance, and 
' choose a type (in this case, string)
Dim List As New GenericList(Of String)

' Add two strings.
List.Add("blue")
List.Add("green")

' The next statement will fail because it has the 
' wrong type. There is no automatic way to convert 
' a Guid object to a string. In fact, this line 
' won't ever run, because the compiler notices the 
' problem and refuses to build the application.
List.Add(Guid.NewGuid())

Não há limites para o número de maneiras de parametrizar uma classe. No exemplo GenericList, há somente um parâmetro de tipo. No entanto, é fácil criar uma classe que funcione com dois ou três tipos de objetos e permita que se tornem genéricos os dois tipos. Para usar essa abordagem, separe cada tipo com uma vírgula (entre colchetes no início de uma classe).

Use como exemplo a seguinte classe GenericHashTable. Ela permite definir o tipo que você deseja usar para os itens a serem armazenados (ItemType) e as chaves que você está usando para indexar tipos (KeyType).

Public Class GenericHashTable(Of ItemType, KeyType)
    Inherits DictionaryBase
    ' (Code goes here.)
End Class

Outro recurso importante das coleções genéricas é a habilidade de usar restrições, que podem limitar os tipos permitidos. Por exemplo, você pode desejar criar uma classe que dê suporte qualquer tipo que atenda a uma determinada interface. Nesse caso, após declarar o espaço reservado para o tipo, adicione a palavra-chave As, seguida da classe de base ou da interface que o tipo precisa ter.

Segue um exemplo que restringe a classe GenericList para que use somente itens serializáveis. (Um motivo pelo qual você pode usar essa abordagem é se desejar adicionar outro método em GenericList que exija serialização, por exemplo, um método que grave todos os itens em um fluxo.)

Public Class GenericList(Of ItemType As ISerializable)
    Inherits CollectionBase 
    ' (Code goes here.)
End Class

Você pode definir quantas restrições desejar, contanto que separe cada classe ou interface com o símbolo &. Restrições são impostas pelo compilador.

Conseqüentemente, os designers do .NET Framework são bem conscientes da utilidade de coleções genéricas e eles já criaram várias delas que você pode usar. Você as encontrará no novo espaço para nome Systems.Collections.Generic. São elas:

  • List – uma coleção básica como o exemplo GenericList.

  • Dictionary – uma coleção de valor de nome que indexa cada item com uma chave.

  • LinkedList – uma lista vinculada, onde cada item aponta para o próximo item na cadeia.

  • Queue – uma coleção em que o primeiro a entrar é o primeiro a sair.

  • Stack – uma coleção em que o último a entrar é o primeiro a sair.

  • ReadOnlyCollection – uma coleção com um conjunto fixo de itens que não podem ser alterados após serem criados.

  • SortedDictionary – uma coleção de valor de nome que é mantida sempre em ordem de classificação.

A maior parte desses tipos duplica um dos tipos no espaço para nome System.Collections. As coleções antigas permanecem para garantir compatibilidade com versões anteriores.

Conclusão

Como você viu neste artigo, as mudanças na linguagem na nova versão do VB são aprimoramentos que simplificam a vida sem tornar obsoleto nenhum código existente. Muitas dessas mudanças são recursos de linguagem secundários importados do C# (como sobrecarga de operador), enquanto outros são ingredientes completamente novos que foram compilados na nova versão do Common Language Runtime (como as coleções genéricas). Obviamente, a linguagem VB é viva e ainda está sujeita a mudar para melhor.

Para obter mais informações sobre a Hardcore Visual Studio e a Pinnacle Publishing, visite o site http://www.pinpub.com/

Observação: esse não é um site da Microsoft Corporation. A Microsoft não é responsável pelo conteúdo.

Este artigo é uma reprodução da edição de novembro de 2004 da Hardcore Visual Studio. Copyright 2004, Pinnacle Publishing, Inc., a menos que observado em contrário. Todos os direitos reservados. A Hardcore Visual Studio é uma publicação independente da Pinnacle Publishing, Inc. Nenhuma parte deste artigo pode ser usada ou reproduzida de nenhuma maneira (exceto em citações resumidas usadas em artigos ou resenhas) sem consentimento prévio da Pinnacle Publishing, Inc. Para entrar em contato com a Pinnacle Publishing, Inc., ligue para 1-800-788-1900.

© .

Page view tracker