Twitter
RSS

Extension Methods - (Parte 2)

1
Como foi visto no post anterior, os Extension Methods são uma nova feature do .NET Framework 3.5, mas isso não significa que esse recurso pode ser usado somente nessa versão do Framework. Neste post veremos como utilizar os Extension Methods em aplicações com o Framework 2.0.

Para utilizar os Extension Methods em aplicações ou bibliotecas que são compiladas com o Framework 2.0 teremos que criar nosso próprio atributo do tipo System.Runtime.CompilerServices.Extension que é encontrado no assembly System.Core.dll. Ao criar um aplicação para o Framework 2.0 você irá notar que não poderá referenciar o assembly System.Core.dll pois o mesmo só pode ser utilizado para o Framework 3.5.

Então para poder utilizar os Extension Methods em aplicações 2.0, teremos que criar nosso próprio atributo, para criá-lo, basta digitar o seguinte código em seu projeto:
using System;
using System.Text;
using System.Timers;
using System.Runtime.CompilerServices;

namespace ExtensionMethods
{
   static class Program
   {
      // DisplayInterval é um ExtensionMethod que estende a classe Timer
      static void DisplayInterval(this Timer t)
      {
         Console.WriteLine(t.Interval.ToString());
      }

      static void Main(string[] args)
      {
         System.Timers.Timer t = new System.Timers.Timer(2000);
         // Chama o método DisplayInterval normalmente
         Program.DisplayInterval(t);
         // Chama o método DisplayInterval estendido
         t.DisplayInterval();
         // Espera uma ação do usuário
         Console.ReadLine();
      }
   }
}

// Cria o namespace CompilerServices com a classe ExtensionAttibute
// que existe somente em System.Core.dll no .Net Framework 3.5
namespace System.Runtime.CompilerServices
{
   public class ExtensionAttribute : Attribute
   {
   }
}

No código acima criamos o namespace System.Runtime.CompilerServices com a classe ExtensionAttribute que herda da classe Attribute, isso fará com que o código da aplicação imite o código do assembly System.Core. Isto já será o suficiente para usarmos os Extension Methods em uma aplicação com o Framework 2.0. No código acima temos o método DisplayInterval que é um Extension Method que estende a classe System.Timers.Timer.

Como uma das principais caracteristicas dos Extension Methods é estender as funcionalidades das classes sem estar mechendo diretamente no código fonte original nada melhor do que poder usá-lo em aplicações mais antigas, logo, aplicações que ainda usam o Framework 2.0 ao invés do 3.5

Para finalizar, vale resaltar que essa técnica não pode ser aplicada em aplicações ASP.NET que são compiladas no Framework 2.0, pois os Web servers ao efetuarem o deploy não entendem os Extension Methods.

Até a próxima...


Extension Methods - C# 3.0 & Visual Basic 9.0

0
Com o tradicional desenvolvimento orientado a objeto, quando se deseja herdar funcionalidades de uma classe, é necessário criar uma classe que estenda a classe base. Visual Basic e C# suportam esse conceito de orientação a objeto, mas algumas vezes, podemos ter classes marcadas como "NotInheritable" ou "sealed" para previnir que sejam feitas modificações no comportamento da classe através da herança. Como resultado não podemos customizar essas classes. Um exemplo disso é a classe System.String do .Net Framework.

Uma nova feature encontrada no C# 3.0 e Visual Basic 9.0 permite que você estenda as funcionalidades de um tipo existente que não permite utilizar herança. O responsável por isso são os Extension Methods(Extensão de médotos) que tem papel crucial na implementação do LINQ.

Extension Methods provém um mecanismo simples para estender tipos do sistema(valor, referência, tipos interface) com novos métodos. Os métodos estendidos, estendem as funcionalidades de um tipo original, podendo ser chamado normalmente. Eles criam a ilusão de que eles são definidos no tipo real, mas na realidade, não há alteração no tipo original.

Isto não é um conceito padrão de orientação a objeto, isto é uma feature do .Net Framework, que gera um código no Intermediate Language(IL) através do compilador gerando uma chamada compartilhada a um método.

O código abaixo mostra os Extension Methods "AlternateCase" e " IsValidEmailAddress" que estendem a classe System.String que é marcada como "NotInheritable" ou "sealed". O primeiro método retorna uma string com seus caracteres minúsculos e maiúsculos alternadamente. Já o segundo método retorna um valor booleano que resulta da validação de um e-mail.

'VB.NET
Imports System.Runtime.CompilerServices
Imports System.Text.RegularExpressions

Module Module1
   Sub Main()
      Dim mail As String = "test@test.com"
      'Chama método AlternateCase como uma extensão da variável mail
      Console.WriteLine("mail.AlternateCase: " & mail.AlternateCase)
      'Chama o método sem ser uma extensão da variável
      Console.WriteLine("Extensions.AlternateCase(mail): " &
      Extensions.AlternateCase(mail))
      'Chama método IsValidEmailAddress como uma extensão da variável mail
      Console.WriteLine("mail.IsValidEmailAddress(): " & mail.IsValidEmailAddress)
      'Chama o método sem ser uma extensão da variável
      Console.WriteLine("Extensions.IsValidEmailAddress(mail): " &
      Extensions.IsValidEmailAddress(mail))
      'Espera ação do usuário 
      Console.Read()
    End Sub
End Module

Module Extensions 
    <Extension()>_
    Function AlternateCase(ByVal x As String) As String
       Dim b = False
       Dim SB As New System.Text.StringBuilder
       For Each c In x
          If b Then
             SB.Append(Char.ToUpper(c))
          Else
             SB.Append(Char.ToLower(c))
          End If
          b = Not b
       Next
       Return SB.ToString
    End Function

    <Extension()>_
    Function IsValidEmailAddress(ByVal x As String)As Boolean
        Dim regex As Regex = New Regex("^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$")
        Return regex.IsMatch(x)
    End Function
End Module

Note que no Visual Basic é necessário importar o namespace System.RunTime.CompilerServices e utilizar o Annotation "Extension()" em cima do método desejado.

//C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions; 

namespace ExtensionCSharp
{
   class Program
   {
       static void Main(string[] args)
       {
           String mail = "test@test.com";
           //Chama método AlternateCase como uma extensão da variável mail
           Console.WriteLine("mail.AlternateCase(): " + mail.AlternateCase());
           //Chama o método sem ser uma extensão da variável
           Console.WriteLine("Extensions.AlternateCase(mail): " + Extensions.AlternateCase(mail));
           //Chama método IsValidEmailAddress como uma extensão da variável mail 
           Console.WriteLine("mail.IsValidEmailAddress(): " + mail.IsValidEmailAddress());
           //Chama o método sem ser uma extensão da variável
           Console.WriteLine("Extensions.IsValidEmailAddress(mail): " + Extensions.IsValidEmailAddress(mail));
           //Espera ação do usuário 
           Console.Read();
        }
    }

    public static class Extensions
    {
        public static String AlternateCase(this String x)
        {
            Boolean b = false;
            StringBuilder SB = new StringBuilder();
            foreach(Char c in x)
            {
               if (b)
                  SB.Append(Char.ToUpper(c));
               else
                  SB.Append(Char.ToLower(c));
               b = !b;
            }
            return SB.ToString();
         }

         public static bool IsValidEmailAddress(this String x)
         {
             Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
             return regex.IsMatch(x);
         }
    }
}

Note que em C# também se faz necessário importar o namespace System.RunTime.CompilerServices contido no assemblie System.Core.dll. Porém em C# para identificar o método como um Extension Methods não precisa utilizar Annotations, basta utilizar a palavra reservada "this" na declaração do parâmetro no método, como mostra o código acima.

Após o Extension Method ser criado, ele já fica associado a classe que ele estendeu, neste exemplo a classe System.String foi estendida, e o método pode ser visualizado através do IntelliSense do Visual Studio como mostra a figura abaixo:

Você irá ver que os Extension Methods aparecem com um ícone diferente(flecha azul apontando para baixo) para diferenciá-los dos métodos instanciados pela classe.

As vantagens em usar Extension Methods é que ele permite adicionar funcionalidades para o tipo que você deseja customizar sem que você quebre o código de aplicações já existentes. Você pode estender interfaces padrões com métodos adicionais sem alterar fisicamente a biblioteca de classes existentes.

No próximo post, pretendo mostrar outras vantagens de utilizar Extension Methods e também mostrar um macete para utilizá-lo em projetos com o .Net Framework 2.0

Para baixar a solução completa deste exemplo clique aqui (VS2008).

Até a próxima...