As novidades do Visual Basic 2005

Estamos no ano do Visual Basic 2005, já é hora de conhecermos mais a fundo as novidades que estão por vir. Vamos então direto ao assunto.

Partial types

Partial types é um recurso adicionado na síntaxe da orientação a objetos que permite uma utilização bem mais ampla do famoso code-behind que já estavamos acostumados a utilizar em ASP 3.

A palavra Partial pode ser adicionada a uma classe, permitindo que o código da classe seja dividido em duas partes. Assim sendo, se temos uma classe grande, podemos dividi-la em vários arquivos utilizando a palavra partial. No momento da compilação o compilador vai juntar tudo e interpretar como uma única classe.

Veja um exemplo do código interno de um form separado do código da aplicação com a palavra chave Partial :

Partial Public Class Form1
    Inherits System.Windows.Forms.Form

    _
    Public Sub New()
        MyBase.New()
         'This call is required by the Windows Form Designer.
        InitializeComponent()
    End Sub

    'Form overrides dispose to clean up the component list.

    _
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If

        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer

    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer

    'It can be modified using the Windows Form Designer. 

    'Do not modify it using the code editor.

    _
    Private Sub InitializeComponent()
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(427, 410)
        Me.Name = "Form1"
        Me.Text = "Form1"
    End Sub

End Class

  

Níveis de acesso mistos

Isso é um recurso que faltou no VB.NET 2003 e agora está sendo trazido de volta (sim, de volta, pois VB 6 tinha).

Digamos que vamos criar uma propriedade com valor calculado, INSS, por exemplo. Desejaremos que esta propriedade seja somente de leitura.

Mas internamente pode ser útil que seja possível a escrita na propriedade INSS, para permitir a realização de cálculos. Neste caso o GET e o SET da propriedade passam a ter visibilidades distintas : Enquanto o GET é público, o SET é privado ou protected.

    Public Property inss() As Decimal
        Get 

        End Get
        Protected Set(ByVal value As Decimal) 

        End Set
    End Property

  

Bloco using

O Blogo usign se assemelha muito ao bloco try/finally, mas com uma síntaxe mais enxuta. Através do bloco using podemos definir variáveis que serão usadas em um pequeno trecho de código e que depois serão eliminadas, sendo chamado o dispose nos casos em que o objeto implementar a interface Idisposable.

        Using cn As New OleDb.OleDbConnection, cmd As New OleDb.OleDbCommand(cn)
            cn.ConnectionString = ""
            cmd.CommandText = ""
            cmd.ExecuteNonQuery()
            cn.Close()
        End Using

Custom events

Os eventos definidos em uma classe permitem que o usuário da classe associe tratadores de eventos utilizando a instrução addhandler, bem como também permitem que o usuário remova tratadores de evento utilizando o remove handler.

Um evento pode ter diversos tratadores de evento, um único tratador de evento ou nenhum tratador de evento.

Então, o que é um Custom event ? Um Custom Event é uma nova síntaxe de criação de eventos que permite que o programador personalize o processo de adição de um tratador de eventos, a exclusão de um tratador de eventos e o processo de disparo de um evento.

Existem 2 motivos principais para que se deseje personalizar um evento : otimizar o consumo de memória e permitir processamento assíncrono. Vamos falar dos dois motivos separadamente.

Otimizar o consumo de memória

Para cada evento tradicional de uma classe o .NET mantém em memória uma variável para guardar os tratadores de eventos desta classe. Se uma classe tem 10 eventos, mesmo que os 10 não estejam sendo utilizados haverão em memória 10 variáveis controlando os 10 eventos.

Utilizando custom events esse processo pode ser otimizado, reduzindo o consumo de memória. Por exemplo, podemos criar uma única variável para guardar os tratadores de todos os eventos da classe, economizando assim 9 variáveis.

Veja abaixo um exemplo simples. Este exemplo apenas permite um único tratador de evento por evento. Veja o código :

 

Public Class MemoryOptimizedBaseControl
    ' Define a delegate store for all event handlers.
    Private Events As New System.ComponentModel.EventHandlerList

     ' Define the Click event to use the delegate store.
    Public Custom Event Click As EventHandler
        AddHandler(ByVal value As EventHandler)
            Events.AddHandler("ClickEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Events.RemoveHandler("ClickEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal ea As EventArgs)
            CType(Events("ClickEvent"), EventHandler).Invoke(sender, ea)
        End RaiseEvent
    End Event

    ' Define the Click event to use the same delegate store.
    Public Custom Event DoubleClick As EventHandler
        AddHandler(ByVal value As EventHandler)
            Events.AddHandler("DoubleClickEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Events.RemoveHandler("DoubleClickEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal ea As EventArgs)
            CType(Events("DoubleClickEvent"), EventHandler).Invoke(sender, ea)
        End RaiseEvent
    End Event

    ' Define additional events to use the same delegate store.
    ' ...
End Class

 

Permitir execução assíncrona

Quando existem diversos tratadores de evento para um mesmo evento, os tratadores são chamados sequencialmente. Essa chamada sequencial pode prejudicar a performance da aplicação e tarefas mais críticas.

Criando um custom event e personalizando o processo de disparo dos tratadores podemos fazer com que todos os tratadores de um determinado evento sejam disparados em paralelo de forma assíncrona, ao invés de serem disparados sequencialmente.

Veja como fica o código :
 

Public NotInheritable Class ReliabilityOptimizedControl
    'Defines a list for storing the delegates

    Private EventHandlerList As New ArrayList

    'Defines the Click event using the custom event syntax.
    'The RaiseEvent always invokes the delegates asynchronously

    Public Custom Event Click As EventHandler
        AddHandler(ByVal value As EventHandler)
            EventHandlerList.Add(value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            EventHandlerList.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal ea As EventArgs)
            For Each handler As EventHandler In EventHandlerList
                If handler IsNot Nothing Then
                    handler.BeginInvoke(sender, ea, Nothing, Nothing)
                End If
            Next
        End RaiseEvent
    End Event
End Class

Juntando tudo

Claro que podemos implementar os dois recursos em uma única classe, fazendo com que os eventos gerem economia de memória e disparo assíncrono. Veja como fica o código :

Public NotInheritable Class ReliabilityOptimizedControl
    'Defines a list for storing the delegates

    Private ListaEventos As New hashtable

    'Defines the Click event using the custom event syntax.
    'The RaiseEvent always invokes the delegates asynchronously

    Public Custom Event Click As EventHandler
        AddHandler(ByVal value As EventHandler)
            Dim el As arrayList

            If isnothing(listaeventos("Click")) Then
                el = New arraylist
                listaeventos.add("Click", el)
            Else
                el = listaeventos("Click")
            End If

            el.Add(value)
            listaeventos("Click") = el
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Dim el As arraylist

            If isnothing(listaeventos("Click")) Then
                el = New arraylist
                listaeventos.add("Click", el)
            Else
                el = listaeventos("Click")
            End If

            el.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal ea As EventArgs)
            Dim el As arraylist

            If isnothing(listaeventos("Click")) Then
                el = New arraylist
                listaeventos.add("Click", el)
            Else
                el = listaeventos("Click")
            End If

            For Each handler As EventHandler In el
                If handler IsNot Nothing Then
                    handler.BeginInvoke(sender, ea, Nothing, Nothing)
                End If
            Next
        End RaiseEvent
    End Event
End Class

 

Generics

A melhor forma de compreendermos a necessidade dos Generics é analisando o processo de criação de uma coleção fortemente tipada.

Para criarmos uma coleção precisamos fazer uma herança da classe collectionBase. Veja um pequeno exemplo de como fica :
 

Public Class ColecaoFuncionarios
    Inherits system.Collections.CollectionBase

    Public Sub Adicionar(ByVal item As funcionario)
        innerlist.add(item)
    End Sub

    Public Function item(ByVal index As Integer) As funcionario
        Return (innerlist(index))
    End Function

    ...  

End Class

O problema de coleções assim é que para cada tipo de dados para o qual desejarmos ter uma coleção precisaremos codificar uma classe coleção diferente. Se tentarmos utilizar uma classe coleção única seremos obrigados a trocar a definição de tipo "funcionario" por Object.

A criação de uma coleção de Objects nos traz dois problemas :

  • A coleção aceitará qualquer coisa, um objeto de tipo indesejado pode vir a ser inserido na coleção, causando erro na aplicação.
  • Se utilizarmos a coleção de Objects para armazenar valores inteiros então estaremos armazenando inteiros em variáveis object. Inteiros são dados do tipo Value Type, Object é uma informação do tipo reference type. Esse tipo de atribuição causa um processo que é chamado de boxing e unboxing e gera perda de performance.

Então como solucionar esse impasse ? A solução é exatamente o uso de Generics. Através da síntaxe dos Generics é possível solucionarmos o problema, criando uma coleção que funcione para qualquer tipo de dados. Veja como fica :

Public Class ColecaoQualquerCoisa(Of tipo)
    Inherits system.Collections.CollectionBase

    Public Sub Adicionar(ByVal item As tipo)
        innerlist.add(item)
    End Sub

    Public Function item(ByVal index As Integer) As tipo
        Return (innerlist(index))
    End Function

    ...  

End Class

Neste trecho de código "tipo" vira uma incógnita que só é definida no momento de uso desta coleção. Veja como fica o código para utilizar esta coleção :

        Dim col As New ColecaoQualquerCoisa(Of funcionario)
        Dim col2 As New ColecaoQualquerCoisa(Of Integer)

A variável col neste exemplo passa a ser uma coleção de funcionários, fortemente tipada, nenhum outro tipo de dados poderá ser inserido na coleção. Já a variável col2 passa a ser uma coleção de inteiros, sem causar boxing e unboxing. Tudo resolvido com a definição da classe colecaoqualquercoisa uma única vez, sem a necessidade de recodificação.

Os generics não precisam ser utilizados apenas na criação de classes. Um outro bom exemplo do uso de Generics é justamente o código para exibição dos dados da coleção. Neste caso o código pode ser uma simples sub, veja como fica :

     Sub Exibir(Of tipo)(ByVal col As CollectionBase)
        Dim item As tipo

        For Each item In col
            msgbox(item.tostring)
        Next
    End Sub

Para utilizar o método :

Exibir(Of funcionario)(col)

Overload de operadores

Quando falo de operadores estou me referindo realmente aos operadores matématicos, +,-,/,*,etc...

Além destes o ctype também é considerado um operador.

Quando aplicamos estes operadores sobre números, por exemplo, temos o resultado esperado. Mas o que ocorre se aplicamos esses operadores sobre classes personalizadas ?

É justamente ai que entra o overloads de operadores. Através deste novo recurso da linguagem podemos definir qual o efeito que um determinado operador terá sobre uma classe. Podemos então fazer operações matemáticas com as classes. Por exemplo, para adicionar uma classe em uma coleção podemos simplesmente utilizar o operador de adição (+) desde que a coleção tenha definido este operador.

Vejamos um exemplo com o ctype. Veja o trecho abaixo :

        Dim f As New funcionario
 ...
         MsgBox(f)

Normalmente teremos um erro na linha do msgbox, acusando não ser possível converter a classe para string. Digamos que utilizemos o ctype :

        MsgBox(CType(f, Decimal))

Não melhorou muito, pois ainda assim teremos a mensagem de que não é possível converter funcionário para decimal.

Porém podemos, dentro da classe funcionário, definir a ação que será feita pelo operador ctype e desta forma fazer com que esta instrução passe a ser válida. Veja como fica um pequeno exemplo :

Class funcionario

...

    Public Shared Narrowing Operator CType(ByVal obj As funcionario) As Decimal
        Return (obj.salliq)
    End Operator

...

End Class

Com este código estamos dizendo que sempre que houver uma tentativa de fazer o ctype de funcionário para decimal deve ser devolvido o salário líquido do funcionário.

Podemos fazer overloads dos outros operadores e desta forma poder manipular as classes utilizando os operadores matemáticos.

 

Dennes Torres
MCAD,MCSD,MCSE,MCDBA