Métodos de extensión

Artículo sobre los métodos de extensión en C#


En ocasiones nos encontramos que alguna de las clases que usamos carece de un método que nos ayudaría mucho en el desarrollo bien porque se utiliza a menudo o bien porque nos interesa que esté asociado a un tipo pero no está implementado en la definición.

En esos casos lo normal es crear una clase heredada que implemente estos métodos. Lo malo es que en ciertas ocasiones no podremos hacerlo como cuando la clase no permite herencia porque esté definida como sealed.

En estos casos podemos crear una clase de ayuda estática, conocidas como wrapper o helpers, para almacenar las funciones relacionadas. Es decir, si por ejemplo nos interesa tener una función que devuelva el doble de un número podemos utilizar una clase como ésta:

public static class IntUtils
{
	public static int Doble(int intValue)
	{ return 2 * intValue;
	}
}

Esta es una opción correcta en cualquier caso pero nos surge un problema de visibilidad en el código. En principio la clase que acabamos de crear (IntUtils) no se relaciona directamente con la clase int de modo que los programadores no sabrán que existe un método que opera sobre la clase int situado en la clase IntUtils que sirve para doblar un número.

Para solucionar este problema en el Framework 3.0 aparecen los métodos de extensión que permiten añadir métodos a clases existentes sin necesidad de utilizar la herencia. Es decir, nos ofrecen una alternativa para codificar el caso anterior que posteriormente nos facilita su utilización (entre otras cosas mediante Intellisense).

Para crear un método de extensión simplemente debemos crear un método estático, en una clase definida también como estática, de este tipo:

public static class IntUtils
{
	public static int Doble(this int intValue)
	{ return 2 * intValue;
	}
}

Observe que el parámetro de tipo cadena de la función se prefija con la palabra reservada this. Al incluir antes del primer parámetro, denominado Parámetro de Instancia, esta palabra reservada, el compilador convierte automáticamente esta función en un método de extensión del tipo del parámetro de instancia. Es decir, en este caso hemos creado un método de extensión para el tipo int que podemos llamar de la siguiente forma:

	int intValue = 5.Doble();

Que asignaría a la variable intValue el valor 10.

Lo bueno es que, a partir de su definición, el método aparece en la lista de Intellisense cada vez que utilicemos un entero aumentando la visibilidad del código.

Aparte del parámetro con el tipo, podemos utilizar otros parámetros en los métodos de extensión. Por ejemplo, si queremos implementar una función Left sobre cadenas que nos devuelvan los primeros n caracteres de una cadena podemos definir el siguiente método:

public static class CutStrings
{
	public static string Left(this string strValue, int intChars)
	{ 	if (!string.IsNullOrEmpty(strValue) && strValue.Lengt >= intChars)
			return strValue.SubString(0, intChars);
		else
			return strValue;
	}
}

Al que llamaríamos utilizando la siguiente instrucción:

	string str = Client.LastName.Left(4);

Lo único que debemos tener en cuenta es que el primer parámetro siempre tiene que ser del tipo que deseamos ampliar mientras que el resto de parámetros se comportan de forma normal.

Los métodos de extensión, aparte de aumentar la productividad y la visibilidad del código, se utilizan como base para Frameworks como Linq

Como hemos visto los métodos de extensión son realmente características del compilador. El compilador expande las llamadas a métodos de extensión de forma que el código IL generado utilizando métodos de extensión es equivalente al generado cuando utilizamos un método estático normal.

En cualquier caso, al utilizar métodos de extensión debemos considerar los siguientes puntos:

  • Si la clase ya tiene un método con el mismo nombre que el método de extensión el que utilizará la aplicación será el método de la instancia, no el de extensión.
  • No se deberían utilizar métodos de extensión sobre clases que pueden heredarse. La forma correcta en este caso sería extender toda la clase.
  • Los métodos de extensión no pueden acceder a miembros privados de la clase.

El primer punto de la lista anterior puede dar lugar a problemas difíciles de localizar. El compilador no avisa en ningún momento si existe un método en la clase con el mismo nombre: al fin y al cabo es una construcción totalmente correcta al interpretarlo. Sin embargo, en ejecución puede que la aplicación utilice el método de la clase cuando esperábamos que utilizar se método de extensión.

Ocurre algo similar cuando tenemos un método de extensión con el mismo nombre para el mismo tipo definido en dos espacios de nombres diferentes. Quien decide realmente el método de extensión a utilizar es la cláusula using colocada en la cabecera lo que puede resultar poco intuitivo y algo arriesgado en las modificaciones.

Además, debemos recordar que los métodos de extensión son independientes de la clase a la que extienden por lo que la clase puede evolucionar sin tener en cuenta los métodos que trabajan sobre ella provocando errores que nos pueden dar más de un dolor de cabeza.

Páginas relacionadas