Muchas veces hemos utilizado atributos en C# para miembros o clases. En este post vamos a ver cómo crear nuestros propios atributos que nos van a permitir añadir información extra a algunos elementos.

Vamos a trabajar con atributos para enriquecer un poco a los enumerados, que muchas veces se nos quedan un poco cortos.

Algunos ejemplos

Hay una gran cantidad de ejemplos de atributos que hemos podido utilizar alguna vez: Newtonsoft Json, Entity Framework, xUnit, etc.

 

Antes de empezar…

Para crear un atributo debemos crear una clase que herede de Attribute. Además podemos especificar algunas propiedades utilizando el atributo AttributeUsage .

Con el atributo AttributeUsage podemos indicar:

  • ValidOn: targets válidos para el atributo que vamos a crear. Aquí podemos consultar los posibles valores.
  • AllowMultiple: nos indica si el atributo se puede especificar más de una vez para un mismo elemento. Por defecto false.
  • Inherited: nos indica si el atributo lo pueden heredar las clases derivadas. Por defecto false.

Antes de seguir debemos hacer una diferenciación entre parámetros posicionales y parámetros con nombre.

Los parámetros posicionales son obligatorios y los especificaremos en el constructor del atributo mientras que los parámetros con nombre, que son opcionales, los especificaremos como propiedades y deberemos nombrarlos al usar el atributo.

Nota: Los parámetros de los atributos deben ser un valor constante de un tipo simple, string, enumerado, Type…

Aquí puedes leer la documentación de los atributos.

Creando nuestro primer atributo: Display Name

Uno de los problemas habituales con los enumerados es cuando los quieres mostrar por pantalla para, por ejemplo, que el usuario pueda filtrar los datos por ese valor del enumerado.

Probablemente todos hayamos hecho algo como un switch para mostrar un texto u otro. Pues bien, esto lo podemos resolver fácilmente con un atributo.

En este ejemplo vamos a tener un solo parámetro posicional llamado DisplayName que será el texto a mostrar.

Para crear nuestro atributo DisplayName debemos crear una clase que herede de Attribute. Como vamos a añadirle este atributo a cada valor de enumerado le indicaremos que el target es Field.

Y simplemente añadimos por constructor el display name. Ya podemos utilizar nuestro atributo DisplayName.

Código de ejemplo [GitHub]

Consultando atributos gracias a la reflexión

Ahora que hemos enriquecido nuestro enumerado necesitamos tener una forma de poder consultar la nueva información. Para ello vamos a utilizar reflexión.

Con el siguiente método obtenemos todos los atributos de un determinado tipo del enumerado pasado por parámetro.

Para que sea más sencillo e intuitivo de utilizar vamos a crear un método de extensión que nos devuelva el display name. Simplemente una vez tenemos el atributo obtenemos el miembro público DisplayName.

Ahora podemos utilizar el método GetDisplayName para obtener el DisplayName de un enumerado.

Código de ejemplo [GitHub]

Un ejemplo con parámetros con nombre

En este ejemplo vamos a ver cómo definir atributos con nombre y cómo especificarlos a la hora de usar el atributo.

Como vemos, basta con añadir una propiedad en el atributo para definir un parámetro con nombre.

A la hora de usarlo simplemente nombramos los parámetros y le asignamos un valor después de los parámetros posicionales.

Nota: Este código es un ejemplo para mostrar los parámetros con nombre en un atributo. Hay que tener cuidado a la hora de desarrollar y saber cuándo es necesario o no el uso de los atributos.

Código de ejemplo [GitHub]

Un ejemplo más realista

Ahora vamos a ver un ejemplo algo más realista que inspiró este post. En este caso yo quería mostrar por pantalla unas categorías para el filtrado de datos. Una vez el usuario seleccione una categoría se debe hacer una llamada a una API.

Surgen dos problemas: por un lado por pantalla se debe ver un texto amigable para cada categoría y por otro lado necesitamos saber el valor de cada categoría en el API (que además es un valor multiple).

Para poder mostrar un texto amigable por pantalla para cada categoría basta con añadir el atributo DisplayName que hemos creado antes. Y para obtener el valor que tiene en el API crearemos otro atributo llamado ApiValue. En este caso el atributo ApiValue se podrá asignar varias veces a un elemento.

Ahora simplemente en nuestro enumerado ya podemos especificar el nombre a mostrar y los diferentes valores que tienen las categorías en el API.

Al igual que antes obtenemos la información del enumerado gracias a su atributo.

Aquí podemos ver un ejemplo de ejecución:

Código de ejemplo [GitHub]

Conclusiones

Como hemos visto, los atributos son muy potentes para añadir información a elementos. En nuestro caso lo hemos visto para trabajar con enumerados pero como hemos visto en los ejemplos del principio en C# se pueden utilizar para muchos usos.

Por otro lado no debemos abusar de su uso y siempre debemos plantearnos si tienen sentido o no. Como con todo, hay que tener cuidado al usarlos ya que tenemos que tener en cuenta el abuso de la reflexión, además del abuso decoradores que dificultan la legibilidad del código.

La otra cosa a tener en cuenta es que los parámetros de los atributos solo pueden ser valores constantes y en muchos casos nos puede limitar.

Referencia

Código de ejemplo

https://github.com/maktub82/Samples/tree/master/Attributes

Attributes Tutorial

https://msdn.microsoft.com/en-us/library/aa288454(v=vs.71).aspx

AttributeUsage

https://msdn.microsoft.com/en-us/library/tw5zxet9(v=vs.71).aspx

AttributeTargets Enumeration

https://msdn.microsoft.com/en-us/library/system.attributetargets.aspx

Espero que os haya gustado. Podéis poner en los comentarios otros ejemplos de uso de atributos que hayáis creado. Y nada más. ¡Nos vemos en el futuro!

Sergio Gallardo (@maktub82)