Métodos de extensão – o que preciso, como quero

O que preciso, como quero… mais ou menos. Mais para mais. Comecemos por o básico: o que são métodos de extensão? Em poucas palavras, métodos de extensão são uma forma de injetar funcionalidades escritas por “nós”, personalizadas, diretamente em tipos que tomamos como “fechados”, quer sejam os escritos por a Microsoft ou os escritos por o vizinho de cima. Quando escrevo “injetar funcionalidades” estou-me a referir a métodos implementados por nós que para o Visual Studio fazem parte de determinada classe, e que podem ser chamados a partir de uma instância.

Isto significa que podemos até acrescentar métodos diretamente na classe String ou Integer? Bom, sim!

Note-se que os métodos de extensão não se tratam de material novo! Foram implementados na versão 9 do VB (incluída no Visual Studio 2008). São no entanto, (e por razões que desconheço) pouco conhecidos, se bem que tenha a certeza de que já tenham usado extensões centenas de vezes sem darem por isso. Basta continuar a ler, não só para saber onde tem usado extensões e como é que as pode identificar, mas também para aprender (naturalmente) como as escrever. É fácil. Prometo.

A esta altura é pertinente, com certeza: “como?”

Como devem calcular, não estamos na verdade a escrever o método diretamente na classe alvo. O método é descrito e implementado em um módulo e tem de estar devidamente assinalado como extensão. Por exemplo:

<Extension()>
Public Sub FazerNada(Str As String)
    'faço o que prometo!
End Sub

É um método de extensão da classe String.

A classe Extension fornecida como atributo do método pertence à classe CompilerServices em System.Runtime.CompilerServices. O módulo que usarem para descrever as extensões deverá importar este namespace para facilitar o uso do atributo.

O tipo que o método pretende estender é definido por o tipo do primeiro parâmetro, o que obriga a que um método de extensão tenha, no mínimo, um parâmetro. Se existirem mais parâmetros, já irão fazer parte da assinatura do método. Estamos a falar, portanto, de magia do compilador! O Visual Basic fica a saber que tem de invocar o método como sendo um método auxiliar comum, mas leva automaticamente a referência à instância que originou a invocação no primeiro parâmetro.

Eu tenho o meu módulo de métodos auxiliares. Vou usar extensões para quê?

Os métodos de extensão são, no fundo, uma encarnação dos vossos métodos auxiliares… mas no devido contexto. Só para estarmos a pensar na mesma situação, quando escrevo métodos auxiliares estou a pensar naqueles pequenos métodos que ajudam a desempenhar um ou vários processamentos sobre um tipo, como por exemplo, validar uma String, formatar uma String, verificar se um número é ímpar, remover duplicados de um array, contar o número de palavras de uma String, e por aí adiante. Vou utilizar, a título de exemplo, um método auxiliar que coloca um ponto final em uma frase (String) e uma letra “grande” no início, por exemplo: "gostava de escrever melhor" passa para "Gostava de escrever melhor."

Public Sub Pontuar(ByRef Frase As String)
    Frase =
        Frase(0).ToString.ToUpperInvariant &
        Frase.Substring(1)
    For Each final As String In {".", "!", "?"}
        If Frase.EndsWith(final) Then Exit Sub
    Next
    Frase &= "."
End Sub

Traduzindo este método auxiliar para extensão, ficaria:

<Extension()>
Public Sub Pontuar(ByRef Frase As String)
    Frase =
        Frase(0).ToString.ToUpperInvariant &
        Frase.Substring(1)
    For Each final As String In {".", "!", "?"}
        If Frase.EndsWith(final) Then Exit Sub
    Next
    Frase &= "."
End Sub

Não precisam de procurar diferenças. Eu digo-vos o que mudou: nada. A grande maioria dos métodos auxiliares podem ser traduzidos apenas colocando o atributo que o transforma em extensão. Neste caso, a única diferença nem sequer está relacionada com a implementação em si, trata-se apenas do atributo do método.
Onde é que está então a grande diferença? Existem duas grandes diferenças, mas não estão na forma como se descreve ou implementa o método. Estão na forma como o método é invocado e descoberto.

IntellisenseA primeira grande vantagem de utilizar extensões, em detrimento de métodos auxiliares, é o facto de estas se encontrarem perfeitamente integradas com o Intellisense (as caixas de sugestão de contexto), no contexto do tipo que estão a estender.

Extensão vs métodoSendo frase do tipo String, as sugestões dos membros vão incluir no separador All as nossas extensões desse tipo. Se não fosse extensão, o método teria de ser chamado de forma arbitrária, ou seja, eu teria de me lembrar que existia um método chamado Pontuar.

A segunda grande diferença/vantagem passa também por aqui: não é necessário fornecer a referência a trabalhar, explicitamente. O Visual Basic sabe que a referência a trabalhar é a mesma que provocou a invocação. Isto possibilita invocações em cadeia. Neste caso não é evidente, porque uma segunda chamada ao método Pontuar na mesma referência não vai alterar nada, e também não é possível, dado que o método Pontuar não produz valor, mas a título de exemplo vamos assumir que o método Pontuar é uma Function que devolve uma String que representa a frase pontuada.
Na abordagem sem extensões, aplicar o método duas vezes seria da seguinte forma:

frase = Pontuar(Pontuar(frase))

O resultado da invocação interior era utilizado como alvo de trabalho na invocação exterior. Com extensão, a sintaxe torna-se muito mais natural, e está acompanhada por o Intellisense:

frase = frase.Pontuar.Pontuar()

frase é uma String que é processada com a primeira extensão e produz uma String que é processada com a extensão que lhe sucede.

Existe uma inegável vantagem em utilizar extensões em tudo o que toca, não só à organização e legibilidade do código, mas também ao vosso índice de produtividade. Enfim, todas as vantagens que se podem ter com a utilização do Intellisense, aplicadas a um pedaço de código escrito para trabalhar tipos que não controlamos diretamente. E quem diz métodos auxiliares diz qualquer tipo de método. Manda a necessidade e criatividade.