Posted by: carloslone | December 12, 2008

Extension Methods (Mientras espero en el aereopuerto)

Hace algún tiempo les había ofrecido hablar sobre las virtudes disponibles tanto para c# 3.0 y vb.net 9.0, es decir las nuevas versiones de éstos lenguajes que corren sobre el .net framework 3.5 y Visual Studio 2008. Bien el asunto es que había dejado relegada esta tarea y hoy precisamente (en medio de mi frustración por que el vuelo que tomo a Costa Rica se retraso nada mas que una par de horas) encuentro el tiempo para poder hablarles sobre el tema.

Bien, dejemos de lado la palabrería y entremos en materia. Como desarrolladores, analistas, diseñadores y arquitectos siempre enfrentamos problemas serios al momento de decidir la forma en que podemos producir código reutilizable para nuestras aplicaciones. Los que hemos tenido la oportunidad de programar en .Net sabemos de la infinidad de librerías de clase y métodos (procedimientos y funciones) que nos provee el framework, si bien podemos decir que "casi" todo lo tenemos a la mano, bien sabemos que existen casos en que necesitamos de una función y no la tenemos disponible. Veamos el siguiente ejemplo de una función para poder contar la cantidad de letras dentro de un string:

Vb.Net

   1: Public Function ContarLetrasSinMetodosExtension(ByVal s As String) As Integer
   2:         Dim contadorLetra As Integer
   3:         For i = 0 To s.Length - 1
   4:             If Char.IsLetter(s(i)) Then
   5:                 contadorLetra += 1
   6:             End If
   7:         Next
   8:  
   9:         Return contadorLetra
  10:     End Function

C#

   1: public static int ContarLetrasSinMetodosExtension(string s)
   2:        {
   3:            int contadorLetra = 0;
   4:            for (int i = 0; i <= s.Length - 1; i++)
   5:            {
   6:                if (Char.IsLetter(s[i]))
   7:                    contadorLetra++;
   8:            }
   9:            return contadorLetra;
  10:        }

En el ejemplo anterior vemos una función simple para poder contar el numero de letras presentes en un string, como consideración de usabilidad y diseño podríamos construir una clase con el metodo "shared" (vb.net) o "static" (c#)  en donde podríamos incluir la función. Ok, suena bien, pero no creen que sería mejor utilizar la clase string para esta funcion?, funcionalmente hablando sería más intuitivo para los desarrolladores, al fin y al cabo ellos por lógica el primer lugar donde buscarían esta función seria dentro de la clase string. Bueno cuales son nuestras opciones?, ok la primera sería tener que extender la funcionalidad de la clase string del .net framework, lo cual es una opción viable pero también laboriosa, adicionalmente tenemos el problema que no es una solución transparente, debido a que muchas clases de las librerias de clase del framework han sido marcadas como "sealed". Bien la otra opción es hacer uso de los nuevos Extension Methods, los cales nos permiten extender la funcionalidad de cualquier clase del .net framework, sea esta una clase del framework, una clase de una dll de terceros o bien una clase propia. Bien, veamos a continuación como re-escribiriamos el metodo del ejemplo anterior haciendo uso de los "extension methods":

Vb.Net

   1: <Extension()> _
   2:     Public Function ContarLetrasEnString(ByVal s As String) As Integer
   3:         Dim contadorLetra As Integer
   4:         For i = 0 To s.Length - 1
   5:             If Char.IsLetter(s(i)) Then
   6:                 contadorLetra += 1
   7:             End If
   8:         Next
   9:  
  10:         Return contadorLetra
  11:     End Function

C#

   1: public static int ContarLetrasEnString(this string s)
   2:        {
   3:            int contadorLetra = 0;
   4:            for (int i = 0; i <= s.Length - 1; i++)
   5:            {
   6:                if (Char.IsLetter(s[i]))
   7:                    contadorLetra++;
   8:            }
   9:            return contadorLetra;
  10:        }

Como pueden observar es muy sencillo poder escribir un "extension method", basta con utilizar el atributo <Extension()> y listo. Que pasa en el caso de c#, bueno hay 3  cosas que cambian: en primer lugar tienen que definir explícitamente el método como "static" , en segundo lugar deben agregar la palabra "this" antes del parámetro que identifique el tipo de dato que quieren extender y por ultimo los metodos deben estar definidos dentro de una clase estática.

Como logra el compilador establecer el tipo de dato a extender?, sencillo, el ubica el tipo del primer parámetro del método que esta siendo marcado como<Extesion()> e inmediatamente asocia al tipo de dato el mismo para que pueda aparecer como si fuera un método propio de la clase.

Bien con esto entonces ahora podemos definir una variable tipo string y luego podríamos utilizar el método de extensión, que se ha atachado mágicamente a la clase string, como si fuera uno propio. Veamos el ejemplo:

Metodo de Extension

Dando valor agregado a las clases

Bien hemos visto como de forma tan sencilla podemos extender cualquier clase que definamos dentro del .net framework. Las posibilidades son infinitas, la verdadera tarea de parte nuestra será evaluar el tipo de funciones que podemos extender a una clase con el fin de dar un valor agregado a los desarrolladores. Veamos a continuación que simple y sencillo se convierte la tarea de poder agregarle a la clase string un método para encriptar y obtener un hash del contenido de una cadena de caracteres:

Vb.Net

   1: <Extension()> _
   2:     Public Function ObtenerHashMD5(ByVal s As String) As String
   3:         Dim BytesDatos As Byte() = New UnicodeEncoding().GetBytes(s)
   4:         Dim BytesHash As Byte() = New MD5CryptoServiceProvider().ComputeHash(BytesDatos)
   5:         Return BitConverter.ToString(BytesHash)
   6:     End Function

C#

   1: public static string ObtenerHashMD5(this string s)
   2:        {
   3:            byte[] BytesDatos = new UnicodeEncoding().GetBytes(s);
   4:            byte[] BytesHash = new MD5CryptoServiceProvider().ComputeHash(BytesDatos);
   5:  
   6:            return BitConverter.ToString(BytesHash);
   7:        }

Como ven esta es una función muy simple, pero a la vez muy útil y les permitirá a los desarrolladores generar hashes del contenido de una cadena de caracteres. Como tarea les solicito a escribir una función para descencriptar ¿Cómo lo harían? espero ver en la sección de comentarios implementaciones.

Que Sucede si quiero enviarle parámetros a un método extendido

la respuesta es muy sencilla. Lo único que deberán hacer es agregar luego del primer parámetro de la función (el que identifica el tipo de dato a extender), los parámetros que sean necesarios para llevar a cabo la operación. Veamos a continuación como implementar una función para poder determinar si una string se encuentra dentro de un arreglo de strings:

Vb.Net

   1: <Extension()> _
   2: Public Function BuscarCadenaEnArreglo(ByVal arreglo As String(), _
   3:                                       ByVal cadenaABuscar As String) As Boolean
   4:     For Each s As String In arreglo
   5:         If (s = cadenaABuscar) Then
   6:             Return True
   7:         End If
   8:     Next
   9:     Return False
  10: End Function

C#

   1: public static bool BuscarCadenaEnArreglo(this string[] arreglo, string cadenaABuscar)
   2:        {
   3:            foreach(string s in arreglo)
   4:            {
   5:                if (s == cadenaABuscar)
   6:                    return true;
   7:            }
   8:            return false;
   9:        }

Como ven la operación es muy sencilla, solo agregan parámetros a su método conforme lo vayan necesitando.

Conclusión

Los métodos extendidos (Extension Methodos) son una forma confiable y útil de poder extender la funcionalidad de cualquier clase. Simplifican la operación de crear nuevas operaciones y asociarla a los tipos de datos correspondientes para una mejor usabilididad y descubrimiento.

Bien amigos espero que les halla servido esta breve explicación, les comparto a continuación el proyecto con el código que hemos utilizado:

Proyecto en Vb.NET 

Proyecto en C#   

Saludos,

Carlos A. Lone

Etiquetas de Technorati: ,,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: