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...


O uso do Reflection

0
Um dos maiores benefícios do .NET Framework e do Common Language Run-Time(CLR) em particular é a riqueza dos tipos de informações em runtime. O System.Reflection permite que você navegue e analise um tipo de dados em tempo de execução. Esta capacidade permite desenvolver sistemas mais dinâmicos e que possuam uma arquitetura modular mais clara.

Entre diversas outras funcionalidades, o Reflection nos pertime encapsular os assemblies. Com isso você pode utilizar o Reflection para acessar um assembly que não esteja diretamente referenciado em seu projeto, podendo obviamente, utilizar suas classes e métodos.

O uso mais comum, útil e prático de Reflection é a chamada de métodos dinâmicos, de um assembly externo ou não.

Veja no exemplo abaixo um código que usa Reflection para carregar a classe HttpUtility do assembly System.Web e usar os métodos HtmlEncode e HtmlDecode em um texto HTML:

'Código em VB.NET:
Imports System.Reflection

Public Class Program
   Public Overloads Shared Sub Main()
      Dim path As String = "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Web.dll"
      'Instancia o Assembly através do arquivo
      Dim webAssemby As Assembly = Assembly.LoadFile(path)
      'Recebe o tipo de objeto da classe HttpUtility
      Dim utilType As Type = webAssemby.GetType("System.Web.HttpUtility")
      'Recebe os método estáticos HtmlEncode e HtmlDecode
      Dim encode As MethodInfo = utilType.GetMethod("HtmlEncode", _
      New Type() {GetType(System.String)})
      Dim decode As MethodInfo = utilType.GetMethod("HtmlDecode", _
      New Type() {GetType(System.String)})
      'Cria uma string para codificar
      Dim originalString As String = "Isto é um exemplo do uso de Reflection <.>"
      Console.WriteLine(originalString)
      'Codifica e mostra o valor codificado
      Dim encoded As String = CType(encode.Invoke(Nothing, New Object() {originalString}), String)
      Console.WriteLine(encoded)
      'Decodifica para comparar se funcionou corretamente
      Dim decoded As String = CType(decode.Invoke(Nothing, New Object() {encoded}), String)
      Console.WriteLine(decoded)
   End Sub
End Class

//Código em C#:
using System;
using System.Reflection;

namespace ReflectionCSharp
{
   class Program
   {
       static void Main(string[] args)
       {
           String path = @"C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Web.dll";
           //Instancia o Assembly atrav‚s do arquivo
           Assembly webAssemby = Assembly.LoadFile(path);
           //Recebe o tipo de objeto da classe HttpUtility
           Type utilType = webAssemby.GetType("System.Web.HttpUtility");
           //Recebe os m‚todo est ticos HtmlEncode e HtmlDecode
           MethodInfo encode = utilType.GetMethod("HtmlEncode", new Type[] {typeof(System.String)});
           MethodInfo decode = utilType.GetMethod("HtmlDecode", new Type[] {typeof(System.String)});
           //Cria uma string para codificar
           String originalString = "Isto ‚ um exemplo do uso de Reflection <.>";
           Console.WriteLine(originalString);
           //Codifica e mostra o valor codificado
           String encoded = (String)encode.Invoke(null, new Object[] {originalString});
           Console.WriteLine(encoded);
           //Decodifica para comparar se funcionou corretamente
           String decoded = (String)decode.Invoke(null, new Object[] {encoded});
           Console.WriteLine(decoded);
        }
     }
}

É claro que o Reflection não faz apenas isso, possúi vários recursos interessantes, que vale a pena serem explorados. Mas uma coisa é inegável, as chamadas dinâmicas feitas com o Reflection tornam a lógica da sua aplicação bem mais flexíveis.

Até a próxima...

Builder

0
Definição: Separa a criação do objeto complexo da sua representação. O mesmo processo de construção pode criar diferentes representações.

As classes e/ou objetos que participam deste padrão são:

• Builder (VeiculoBuilder) o especifica uma abstract interface para a criação das partes do objeto tipo Produto • ConcreteBuilder (MotoBuilder, CarroBuilder, CaminhaoBuilder) o constrói e reúne partes do objeto Produto que implementa a interface Builder o define e marca a criação da representação do objeto o fornece uma interface para devolver um objeto do tipo Produto • Director (Concessionaria) o constrói um objeto usando a
interface Builder • Product (Veiculo) o representa o objeto complexo antes da construção. o incluí as classes que definen as partes que constituem o objeto, incluindo interfaces para reunir as partes no resultado final

Builder: Quando e onde usar?
O padrão Builder é um padrão de projeto de criação de objetos que permite o cliente contruir um objeto complexo especificando somente seu tipo e conteúdo. Os detalhes da construção são escondidos inteiramente do cliente. A motivação mais comum para usar o padrão Builder é para simplificar o code do cliente que cria objetos complexos. O Cliente pode apenas especificar as etapas que são necessárias para o Builder criar o objeto, sem ter conhecimento de como o trabalho atual é finalizado. Um cenário aonde você deve considerar o uso do padrão de projeto Builder é quando é deselvolvido um gerador de código. Você pode escrever uma aplicação que cria stored procedures para diferentes tipos de banco de dados(SQL Server, Oracle, DB2). A saída real é bastante diferente, mas as etapas para a criação dos procedimentos contidos no CRUD(Create, Read, Update, Delete) são todos muitos similares.

O padrão Builder não é amplamente usado, mas pode ser encontrado em duas classes do .NET Framework. VBCodeProvider e CSharpCodeProvider criam classes Builder através do método CreateGenerator. O método CreateGenerator retorna uma interface ICodeGenerator onde a geração do código fonte pode ser controlado.

Para ver um exemplo do design pattern Builder na prática é só baixar o projeto de exemplo que segue no link abaixo:
Exemplo padrão Builder (VS2008)

Até a próxima...

Abstract Factory

1
Definição: Fornece uma interface para criar família de objetos relacionados ou dependentes sem especificar a classe concreta deles.

As classes e/ou objetos que participam deste padrão são: • AbstractFactory (ContinenteFactory) o declara uma interface para operações que criam produtos abstratos • ConcreteFactory (AfricaFactory, AmericaFactory) o implementa a operação para criar um objeto produto concreto • AbstractProduct (Herbivoro, Carnivoro) o declara uma interface para um objeto do tipo produto • Product (Gnu, Leao, Bufalo, Lobo) o define um objeto produto para ser criado pela fábrica concreta que implementa a interface AbstractProduct • Client (MundoAnimal) o usa as interfaces declaradas pelas classes AbstractFactory e AbstractProduct

Abstract Factory: Quando e onde usar?
O padrão Abstract Factory provém um cliente com uma classe que cria objetos que possuem relação através de características comuns. Outra razão e benefício para criar objetos usando o padrão Abstract Factory é especialmente quando os contrutores das classes são chamadas diretamentes para criar objetos diferentes(overloads). Construtores são limitados no controle de processos de criação de objetos em geral. Se sua aplicação necessita de maior controle, considere usar um Factory. As Factorys incluem cenários que envolvem caching de objetos, sharing, re-using e aplicações que mantenham contadores de objetos e tipos.

Existe momentos em que o cliente não conhece exatamente que tipo deve construir. Isso pode ser resolvido fácilmente com o uso de tipos base ou interface e a fábrica pode passar parâmetros ou outros tipos de informações para tomar essa decisão para o cliente. Um exemplo disso são os provider especificos dos objetos ADO.NET(DbConnection, DbCommand, DbDataAdapter, etc).

Os contrutores não passam muito bem a sua intenção quando existem vários overloads por que eles necessitam ter o mesmo nome da classe (ou Sub New no VB.NET). Construtores com muitos overloads acabam confundindo os desenvolvedores na hora de decidir qual deles usar. Substituir construtores por métodos factory que mostram a sua intenção muitas vezes é o melhor a fazer.

Exemplo:

Vários construtores sobrecarregados, qual deles você deveria usar?

// C# 
public Veiculo (int passageiros);
public Veiculo (int passageiros, int cavalosPotencia);
public Veiculo (int rodas, bool trailer);
public Veiculo (string tipo);

' VB.NET
Public Sub New(ByVal passageiros As Integer)
Public Sub New(ByVal passageiros As Integer, ByVal cavalosPotencia As Integer)
Public Sub New(ByVal rodas As Integer, ByVal trailer As Boolean)
Public Sub New(ByVal tipo As String) 

Com o padrão Factory o código fica mais expressivo e os desenvolvedores mais produtivos:

// C#
public Veiculo CriaCarro(int passageiros);
public Veiculo CriaSport(int passageiros, int cavalosPotencia);
public Veiculo CriaOnibus(int rodas, bool trailer);
public Veiculo CriaBarco();
public Veiculo CriaMoto();

' VB.NET 
Public Function CriaCarro(ByVal passageiros As Integer) As Veiculo
Public Function CriaSport(ByVal passageiros As Integer, ByVal cavalosPotencia As Integer)As Veiculo
Public Function CriaOnibus(ByVal rodas As Integer, ByVal trailer As Boolean)As Veiculo
Public Function CriaBarco()As Veiculo
Public Function CriaMoto()As Veiculo

Para ver um exemplo do padrão AbstractFactory na prática é só baixar o projeto que segue no link abaixo:

http://cid-17621fcd61d3a95a.skydrive.live.com/self.aspx/Blog/AbstractFactory.zip (VS 2008)

Até a próxima...

Bem-vindo aos padrões de projetos

0
O que é padrão de projeto? Para que servem os padrões de projetos?

Essas são perguntas que todo desenvolvedor deveria fazer antes de inicializar um projeto.

Todos nós já usamos bibliotecas e APIs comerciais. Nós a pegamos, escrevemos alguns códigos, compilamos em nossos programas e aproveitamos um monte de códigos que outra pessoa escreveu. Pense nas APIs e em todas as funcionalidades que elas lhe dão: rede, I/O, etc. As bibliotecas e APIs demoram um tempão para chegar a um modelo de desenvolvimento onde podemos apenas selecionar os componentes e utilizá-los. Mas elas não nos ajudam a estruturar nossos próprios aplicativos de maneiras mais flexíveis, faceis de entender e manter. É aí que os Padrões de Projetos ou Design Patterns entram.

Os padrões de projetos não são nada mais que o uso dos princípios de design orientado a objeto, estão num nível acima das bibliotecas. Os padrões de projetos nos dizem como resolver alguns problemas, e é tarefa nossa adaptar esses designs para adequá-los a nosso aplicativo.

"Mas mesmo que eu siga o encapsulamento e conheça abstração, herança e polimorfismo, preciso mesmo conhecer os Padrões de Projetos? Não é muito direto? não é por isso que fiz todos esses cursos sobre Orientação a Objeto?"

Este é um dos mal-entendidos do desenvolvimento orientado a objeto: que conhecendo os princípios de Orientação a Objetos automaticamente seremos bons na construção de sistemas flexíveis, reutilizáveis e de fácil manutenção. Lembre-se, conhecer conceitos como abstração, herança e polimorfismo não faz de você um bom designer orientado a objeto. Um bom design pensa em como criar designs flexíveis que possam lidar com mudanças.

Obs.: Os padrões de projetos não entram diretamente no seu código, primeiro entram em seu CÉREBRO.

Veja mais sobre padrões de projetos em: http://www.dofactory.com/Patterns/Patterns.aspx http://pt.wikipedia.org/wiki/Padr%C3%B5es_de_projeto_de_software E para descontrair: http://desciclo.pedia.ws/wiki/Gambi_Design_Patterns

Até mais, Hugo Estevam